From d254020f3c4adc9d9bfd7902938e77fa0de91cfe Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 4 Sep 2025 00:44:43 +0200 Subject: [PATCH 01/89] misc --- clippy_lints/src/matches/collapsible_match.rs | 28 +++++++++---------- tests/ui/collapsible_match.rs | 24 ++++++++-------- tests/ui/collapsible_match.stderr | 12 ++++---- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index aaf559fc4439e..11e79333a6948 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -50,15 +50,17 @@ fn check_arm<'tcx>( if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr) && let Some((inner_scrutinee, inner_then_pat, inner_else_body)) = match inner { IfLetOrMatch::IfLet(scrutinee, pat, _, els, _) => Some((scrutinee, pat, els)), - IfLetOrMatch::Match(scrutinee, arms, ..) => if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none()) - // if there are more than two arms, collapsing would be non-trivial - // one of the arms must be "wild-like" - && let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a)) - { - let (then, els) = (&arms[1 - wild_idx], &arms[wild_idx]); - Some((scrutinee, then.pat, Some(els.body))) - } else { - None + IfLetOrMatch::Match(scrutinee, arms, ..) => { + if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none()) + // if there are more than two arms, collapsing would be non-trivial + // one of the arms must be "wild-like" + && let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a)) + { + let (then, els) = (&arms[1 - wild_idx], &arms[wild_idx]); + Some((scrutinee, then.pat, Some(els.body))) + } else { + None + } }, } && outer_pat.span.eq_ctxt(inner_scrutinee.span) @@ -68,8 +70,8 @@ fn check_arm<'tcx>( && !pat_contains_disallowed_or(cx, inner_then_pat, msrv) // the binding must come from the pattern of the containing match arm // .... => match { .. } - && let (Some(binding_span), is_innermost_parent_pat_struct) - = find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id) + && let (Some(binding_span), is_innermost_parent_pat_struct) = + find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id) // the "else" branches must be equal && match (outer_else_body, inner_else_body) { (None, None) => true, @@ -77,9 +79,7 @@ fn check_arm<'tcx>( (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b), } // the binding must not be used in the if guard - && outer_guard.is_none_or( - |e| !is_local_used(cx, e, binding_id) - ) + && outer_guard.is_none_or(|e| !is_local_used(cx, e, binding_id)) // ...or anywhere in the inner expression && match inner { IfLetOrMatch::IfLet(_, _, body, els, _) => { diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 8931a3aa09c61..7a9c8d14d13fb 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -304,18 +304,6 @@ pub fn test_2(x: Issue9647) { } } -// https://github.com/rust-lang/rust-clippy/issues/14281 -fn lint_emitted_at_right_node(opt: Option>) { - let n = match opt { - #[expect(clippy::collapsible_match)] - Some(n) => match n { - Ok(n) => n, - _ => return, - }, - None => return, - }; -} - pub fn issue_14155() { let mut arr = ["a", "b", "c"]; if let Some(last) = arr.last() { @@ -357,6 +345,18 @@ pub fn issue_14155() { } } +// https://github.com/rust-lang/rust-clippy/issues/14281 +fn lint_emitted_at_right_node(opt: Option>) { + let n = match opt { + #[expect(clippy::collapsible_match)] + Some(n) => match n { + Ok(n) => n, + _ => return, + }, + None => return, + }; +} + fn make() -> T { unimplemented!() } diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 14b1c1b187e4f..ebde46cf8a725 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -251,7 +251,7 @@ LL | if let Some(u) = a { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:322:9 + --> tests/ui/collapsible_match.rs:310:9 | LL | / match *last { LL | | @@ -263,7 +263,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:321:17 + --> tests/ui/collapsible_match.rs:309:17 | LL | if let Some(last) = arr.last() { | ^^^^ ---------- use: `arr.last().copied()` @@ -274,7 +274,7 @@ LL | "a" | "b" => { | ^^^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:332:9 + --> tests/ui/collapsible_match.rs:320:9 | LL | / match &last { LL | | @@ -286,7 +286,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:331:17 + --> tests/ui/collapsible_match.rs:319:17 | LL | if let Some(last) = arr.last() { | ^^^^ ---------- use: `arr.last().as_ref()` @@ -297,7 +297,7 @@ LL | &&"a" | &&"b" => { | ^^^^^^^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:342:9 + --> tests/ui/collapsible_match.rs:330:9 | LL | / match &mut last { LL | | @@ -309,7 +309,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:341:17 + --> tests/ui/collapsible_match.rs:329:17 | LL | if let Some(mut last) = arr.last_mut() { | ^^^^^^^^ -------------- use: `arr.last_mut().as_mut()` From a212bd48c41fdcad962edc45f2f1570e5771fd0b Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 4 Sep 2025 01:17:03 +0200 Subject: [PATCH 02/89] misc: put the `: ` in the right place in the struct field suggestion --- clippy_lints/src/matches/collapsible_match.rs | 2 +- tests/ui/collapsible_match.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 11e79333a6948..71d34d705d2b2 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -103,7 +103,7 @@ fn check_arm<'tcx>( // collapsing patterns need an explicit field name in struct pattern matching // ex: Struct {x: Some(1)} let replace_msg = if is_innermost_parent_pat_struct { - format!(", prefixed by `{}`:", snippet(cx, binding_span, "their field name")) + format!(", prefixed by `{}: `", snippet(cx, binding_span, "their field name")) } else { String::new() }; diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index ebde46cf8a725..4d81f91ecc896 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -230,7 +230,7 @@ help: the outer pattern can be modified to include the inner pattern LL | if let Issue9647::A { a, .. } = x { | ^ replace this binding LL | if let Some(u) = a { - | ^^^^^^^ with this pattern, prefixed by `a`: + | ^^^^^^^ with this pattern, prefixed by `a: ` error: this `if let` can be collapsed into the outer `if let` --> tests/ui/collapsible_match.rs:299:9 From fba062b0cae9f92d6e61ebbca0fbbbffdf01bc93 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 4 Sep 2025 01:16:03 +0200 Subject: [PATCH 03/89] fix(collapsible_match): exclude binding modes from struct field pattern suggestions --- clippy_lints/src/matches/collapsible_match.rs | 17 +++++----- tests/ui/collapsible_match.rs | 21 +++++++++++++ tests/ui/collapsible_match.stderr | 31 ++++++++++++++----- 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 71d34d705d2b2..a02e6dd96a1f0 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -13,6 +13,7 @@ use rustc_hir::LangItem::OptionNone; use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_span::Span; +use rustc_span::symbol::Ident; use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or}; @@ -70,7 +71,7 @@ fn check_arm<'tcx>( && !pat_contains_disallowed_or(cx, inner_then_pat, msrv) // the binding must come from the pattern of the containing match arm // .... => match { .. } - && let (Some(binding_span), is_innermost_parent_pat_struct) = + && let (Some((binding_ident, binding_span)), is_innermost_parent_pat_struct) = find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id) // the "else" branches must be equal && match (outer_else_body, inner_else_body) { @@ -103,7 +104,7 @@ fn check_arm<'tcx>( // collapsing patterns need an explicit field name in struct pattern matching // ex: Struct {x: Some(1)} let replace_msg = if is_innermost_parent_pat_struct { - format!(", prefixed by `{}: `", snippet(cx, binding_span, "their field name")) + format!(", prefixed by `{binding_ident}: `") } else { String::new() }; @@ -140,16 +141,16 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { } } -fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: HirId) -> (Option, bool) { - let mut span = None; +fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: HirId) -> (Option<(Ident, Span)>, bool) { + let mut binding = None; let mut is_innermost_parent_pat_struct = false; - pat.walk_short(|p| match &p.kind { + pat.walk_short(|p| match p.kind { // ignore OR patterns PatKind::Or(_) => false, - PatKind::Binding(_bm, _, _ident, _) => { + PatKind::Binding(_bm, _, ident, _) => { let found = p.hir_id == hir_id; if found { - span = Some(p.span); + binding = Some((ident, p.span)); } !found }, @@ -158,7 +159,7 @@ fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: Hi true }, }); - (span, is_innermost_parent_pat_struct) + (binding, is_innermost_parent_pat_struct) } /// Builds a chain of reference-manipulation method calls (e.g., `.as_ref()`, `.as_mut()`, diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 7a9c8d14d13fb..84f958ee84584 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -304,6 +304,27 @@ pub fn test_2(x: Issue9647) { } } +mod issue_13287 { + enum Token { + Name, + Other, + } + + struct Error { + location: u32, + token: Option, + } + + fn struct_field_pat_with_binding_mode(err: Option) { + if let Some(Error { ref token, .. }) = err { + if let Some(Token::Name) = token { + //~^ collapsible_match + println!("token used as a ref"); + } + } + } +} + pub fn issue_14155() { let mut arr = ["a", "b", "c"]; if let Some(last) = arr.last() { diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 4d81f91ecc896..d217948d4ca6c 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -250,8 +250,25 @@ LL | if let Issue9647::A { a: Some(a), .. } = x { LL | if let Some(u) = a { | ^^^^^^^ with this pattern +error: this `if let` can be collapsed into the outer `if let` + --> tests/ui/collapsible_match.rs:320:13 + | +LL | / if let Some(Token::Name) = token { +LL | | +LL | | println!("token used as a ref"); +LL | | } + | |_____________^ + | +help: the outer pattern can be modified to include the inner pattern + --> tests/ui/collapsible_match.rs:319:29 + | +LL | if let Some(Error { ref token, .. }) = err { + | ^^^^^^^^^ replace this binding +LL | if let Some(Token::Name) = token { + | ^^^^^^^^^^^^^^^^^ with this pattern, prefixed by `token: ` + error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:310:9 + --> tests/ui/collapsible_match.rs:331:9 | LL | / match *last { LL | | @@ -263,7 +280,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:309:17 + --> tests/ui/collapsible_match.rs:330:17 | LL | if let Some(last) = arr.last() { | ^^^^ ---------- use: `arr.last().copied()` @@ -274,7 +291,7 @@ LL | "a" | "b" => { | ^^^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:320:9 + --> tests/ui/collapsible_match.rs:341:9 | LL | / match &last { LL | | @@ -286,7 +303,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:319:17 + --> tests/ui/collapsible_match.rs:340:17 | LL | if let Some(last) = arr.last() { | ^^^^ ---------- use: `arr.last().as_ref()` @@ -297,7 +314,7 @@ LL | &&"a" | &&"b" => { | ^^^^^^^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> tests/ui/collapsible_match.rs:330:9 + --> tests/ui/collapsible_match.rs:351:9 | LL | / match &mut last { LL | | @@ -309,7 +326,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> tests/ui/collapsible_match.rs:329:17 + --> tests/ui/collapsible_match.rs:350:17 | LL | if let Some(mut last) = arr.last_mut() { | ^^^^^^^^ -------------- use: `arr.last_mut().as_mut()` @@ -319,5 +336,5 @@ LL | if let Some(mut last) = arr.last_mut() { LL | &mut &mut "a" | &mut &mut "b" => { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ with this pattern -error: aborting due to 16 previous errors +error: aborting due to 17 previous errors From d6890e33e5d5f0e7b5a48b2692047fc824bf3fca Mon Sep 17 00:00:00 2001 From: bendn Date: Thu, 18 Sep 2025 20:20:59 +0700 Subject: [PATCH 04/89] extend while_let_loop to loop { let else } --- clippy_lints/src/loops/mod.rs | 1 + clippy_lints/src/loops/while_let_loop.rs | 30 +++++++++++++++++------- tests/ui/infinite_loops.rs | 2 +- tests/ui/while_let_loop.rs | 13 ++++++++++ tests/ui/while_let_loop.stderr | 29 +++++++++++++++-------- 5 files changed, 55 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 01c36b8cb12fc..7d14aa87d820a 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -861,6 +861,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { // check for `loop { if let {} else break }` that could be `while let` // (also matches an explicit "match" instead of "if let") // (even if the "match" or "if let" is used for declaration) + // (also matches on `let {} else break`) if let ExprKind::Loop(block, label, LoopSource::Loop, _) = expr.kind { // also check for empty `loop {}` statements, skipping those in #[panic_handler] empty_loop::check(cx, expr, block); diff --git a/clippy_lints/src/loops/while_let_loop.rs b/clippy_lints/src/loops/while_let_loop.rs index 845edb9cae158..d4285db0abfcd 100644 --- a/clippy_lints/src/loops/while_let_loop.rs +++ b/clippy_lints/src/loops/while_let_loop.rs @@ -10,19 +10,19 @@ use rustc_hir::{Block, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind, Path, use rustc_lint::LateContext; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { - let (init, let_info) = match (loop_block.stmts, loop_block.expr) { + let (init, let_info, els) = match (loop_block.stmts, loop_block.expr) { ([stmt, ..], _) => match stmt.kind { StmtKind::Let(LetStmt { init: Some(e), - els: None, + els, pat, ty, .. - }) => (*e, Some((*pat, *ty))), - StmtKind::Semi(e) | StmtKind::Expr(e) => (e, None), + }) => (*e, Some((*pat, *ty)), *els), + StmtKind::Semi(e) | StmtKind::Expr(e) => (e, None, None), _ => return, }, - ([], Some(e)) => (e, None), + ([], Some(e)) => (e, None, None), _ => return, }; let has_trailing_exprs = loop_block.stmts.len() + usize::from(loop_block.expr.is_some()) > 1; @@ -38,14 +38,26 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_blo if_let.let_expr, has_trailing_exprs, let_info, - if_let.if_then, + Some(if_let.if_then), ); + } else if els.and_then(|x| x.expr).is_some_and(is_simple_break_expr) + && let Some((pat, _)) = let_info + { + could_be_while_let(cx, expr, pat, init, has_trailing_exprs, let_info, None); } else if let ExprKind::Match(scrutinee, [arm1, arm2], MatchSource::Normal) = init.kind && arm1.guard.is_none() && arm2.guard.is_none() && is_simple_break_expr(arm2.body) { - could_be_while_let(cx, expr, arm1.pat, scrutinee, has_trailing_exprs, let_info, arm1.body); + could_be_while_let( + cx, + expr, + arm1.pat, + scrutinee, + has_trailing_exprs, + let_info, + Some(arm1.body), + ); } } @@ -70,7 +82,7 @@ fn could_be_while_let<'tcx>( let_expr: &'tcx Expr<'_>, has_trailing_exprs: bool, let_info: Option<(&Pat<'_>, Option<&Ty<'_>>)>, - inner_expr: &Expr<'_>, + inner_expr: Option<&Expr<'_>>, ) { if has_trailing_exprs && (needs_ordered_drop(cx, cx.typeck_results().expr_ty(let_expr)) @@ -85,7 +97,7 @@ fn could_be_while_let<'tcx>( // 1) it was ugly with big bodies; // 2) it was not indented properly; // 3) it wasn’t very smart (see #675). - let inner_content = if let Some((pat, ty)) = let_info + let inner_content = if let Some(((pat, ty), inner_expr)) = let_info.zip(inner_expr) // Prevent trivial reassignments such as `let x = x;` or `let _ = …;`, but // keep them if the type has been explicitly specified. && (!is_trivial_assignment(pat, peel_blocks(inner_expr)) || ty.is_some()) diff --git a/tests/ui/infinite_loops.rs b/tests/ui/infinite_loops.rs index 7d01a7fb61fc1..0bde31aca0304 100644 --- a/tests/ui/infinite_loops.rs +++ b/tests/ui/infinite_loops.rs @@ -1,7 +1,7 @@ //@no-rustfix: multiple suggestions add `-> !` to the same fn //@aux-build:proc_macros.rs -#![allow(clippy::never_loop)] +#![allow(clippy::never_loop, clippy::while_let_loop)] #![warn(clippy::infinite_loop)] extern crate proc_macros; diff --git a/tests/ui/while_let_loop.rs b/tests/ui/while_let_loop.rs index 95062c9f46c7d..f28c504742fd7 100644 --- a/tests/ui/while_let_loop.rs +++ b/tests/ui/while_let_loop.rs @@ -22,6 +22,19 @@ fn main() { break; } + loop { + //~^ while_let_loop + let Some(_x) = y else { break }; + } + + loop { + // no error, else branch does something other than break + let Some(_x) = y else { + let _z = 1; + break; + }; + } + loop { //~^ while_let_loop diff --git a/tests/ui/while_let_loop.stderr b/tests/ui/while_let_loop.stderr index ed42628a53e7f..b9aee6eb42ecf 100644 --- a/tests/ui/while_let_loop.stderr +++ b/tests/ui/while_let_loop.stderr @@ -17,6 +17,15 @@ error: this loop could be written as a `while let` loop | LL | / loop { LL | | +LL | | let Some(_x) = y else { break }; +LL | | } + | |_____^ help: try: `while let Some(_x) = y { .. }` + +error: this loop could be written as a `while let` loop + --> tests/ui/while_let_loop.rs:38:5 + | +LL | / loop { +LL | | LL | | LL | | match y { ... | @@ -25,7 +34,7 @@ LL | | } | |_____^ help: try: `while let Some(_x) = y { .. }` error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:34:5 + --> tests/ui/while_let_loop.rs:47:5 | LL | / loop { LL | | @@ -37,7 +46,7 @@ LL | | } | |_____^ help: try: `while let Some(x) = y { .. }` error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:45:5 + --> tests/ui/while_let_loop.rs:58:5 | LL | / loop { LL | | @@ -48,7 +57,7 @@ LL | | } | |_____^ help: try: `while let Some(x) = y { .. }` error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:77:5 + --> tests/ui/while_let_loop.rs:90:5 | LL | / loop { LL | | @@ -68,7 +77,7 @@ LL + } | error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:167:9 + --> tests/ui/while_let_loop.rs:180:9 | LL | / loop { LL | | @@ -88,7 +97,7 @@ LL + } | error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:182:5 + --> tests/ui/while_let_loop.rs:195:5 | LL | / loop { LL | | @@ -107,7 +116,7 @@ LL + } | error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:194:5 + --> tests/ui/while_let_loop.rs:207:5 | LL | / loop { LL | | @@ -126,7 +135,7 @@ LL + } | error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:206:5 + --> tests/ui/while_let_loop.rs:219:5 | LL | / loop { LL | | @@ -137,7 +146,7 @@ LL | | } | |_____^ help: try: `while let Some(x) = Some(3) { .. }` error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:218:5 + --> tests/ui/while_let_loop.rs:231:5 | LL | / loop { LL | | @@ -156,7 +165,7 @@ LL + } | error: this loop could be written as a `while let` loop - --> tests/ui/while_let_loop.rs:230:5 + --> tests/ui/while_let_loop.rs:243:5 | LL | / loop { LL | | @@ -177,5 +186,5 @@ LL + .. LL + } | -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors From 1d7c1afde014291082f5e68f8134581f927482e1 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Fri, 22 Aug 2025 18:57:00 -0700 Subject: [PATCH 05/89] Implement `volatile_composites` lint Volatile reads and writes to non-primitive types are not well-defined, and can cause problems. See https://github.com/rust-lang/rust-clippy/issues/15529 for more details. --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/volatile_composites.rs | 180 +++++++++++++++++++ clippy_utils/src/sym.rs | 2 + tests/ui/volatile_composites.rs | 221 ++++++++++++++++++++++++ tests/ui/volatile_composites.stderr | 89 ++++++++++ 7 files changed, 496 insertions(+) create mode 100644 clippy_lints/src/volatile_composites.rs create mode 100644 tests/ui/volatile_composites.rs create mode 100644 tests/ui/volatile_composites.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 30781d3d33fba..b6b374e26c962 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6798,6 +6798,7 @@ Released 2018-09-13 [`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero [`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask [`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads +[`volatile_composites`]: https://rust-lang.github.io/rust-clippy/master/index.html#volatile_composites [`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons [`waker_clone_wake`]: https://rust-lang.github.io/rust-clippy/master/index.html#waker_clone_wake [`while_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_float diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 7645ac2dd3f7b..6a24aaf59777a 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -780,6 +780,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::visibility::NEEDLESS_PUB_SELF_INFO, crate::visibility::PUB_WITHOUT_SHORTHAND_INFO, crate::visibility::PUB_WITH_SHORTHAND_INFO, + crate::volatile_composites::VOLATILE_COMPOSITES_INFO, crate::wildcard_imports::ENUM_GLOB_USE_INFO, crate::wildcard_imports::WILDCARD_IMPORTS_INFO, crate::write::PRINTLN_EMPTY_STRING_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 51dabee78e9fb..6d6aa310ace9e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -400,6 +400,7 @@ mod useless_conversion; mod vec; mod vec_init_then_push; mod visibility; +mod volatile_composites; mod wildcard_imports; mod write; mod zero_div_zero; @@ -831,5 +832,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom)); store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny)); store.register_late_pass(|_| Box::new(toplevel_ref_arg::ToplevelRefArg)); + store.register_late_pass(|_| Box::new(volatile_composites::VolatileComposites)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/volatile_composites.rs b/clippy_lints/src/volatile_composites.rs new file mode 100644 index 0000000000000..c65b47c0853f4 --- /dev/null +++ b/clippy_lints/src/volatile_composites.rs @@ -0,0 +1,180 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::sym; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{self, Ty, TypeVisitableExt}; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// + /// This lint warns when volatile load/store operations + /// (`write_volatile`/`read_volatile`) are applied to composite types. + /// + /// ### Why is this bad? + /// + /// Volatile operations are typically used with memory mapped IO devices, + /// where the precise number and ordering of load and store instructions is + /// important because they can have side effects. This is well defined for + /// primitive types like `u32`, but less well defined for structures and + /// other composite types. In practice it's implementation defined, and the + /// behavior can be rustc-version dependent. + /// + /// As a result, code should only apply `write_volatile`/`read_volatile` to + /// primitive types to be fully well-defined. + /// + /// ### Example + /// ```no_run + /// struct MyDevice { + /// addr: usize, + /// count: usize + /// } + /// + /// fn start_device(device: *mut MyDevice, addr: usize, count: usize) { + /// unsafe { + /// device.write_volatile(MyDevice { addr, count }); + /// } + /// } + /// ``` + /// Instead, operate on each primtive field individually: + /// ```no_run + /// struct MyDevice { + /// addr: usize, + /// count: usize + /// } + /// + /// fn start_device(device: *mut MyDevice, addr: usize, count: usize) { + /// unsafe { + /// (&raw mut (*device).addr).write_volatile(addr); + /// (&raw mut (*device).count).write_volatile(count); + /// } + /// } + /// ``` + #[clippy::version = "1.92.0"] + pub VOLATILE_COMPOSITES, + nursery, + "warn about volatile read/write applied to composite types" +} +declare_lint_pass!(VolatileComposites => [VOLATILE_COMPOSITES]); + +/// Zero-sized types are intrinsically safe to use volatile on since they won't +/// actually generate *any* loads or stores. But this is also used to skip zero-sized +/// fields of `#[repr(transparent)]` structures. +fn is_zero_sized_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + cx.layout_of(ty).is_ok_and(|layout| layout.is_zst()) +} + +/// A thin raw pointer or reference. +fn is_narrow_ptr<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind() { + ty::RawPtr(inner, _) | ty::Ref(_, inner, _) => inner.has_trivial_sizedness(cx.tcx, ty::SizedTraitKind::Sized), + _ => false, + } +} + +/// Enum with some fixed representation and no data-carrying variants. +fn is_enum_repr_c<'tcx>(_cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + ty.ty_adt_def().is_some_and(|adt_def| { + adt_def.is_enum() && adt_def.repr().inhibit_struct_field_reordering() && adt_def.is_payloadfree() + }) +} + +/// `#[repr(transparent)]` structures are also OK if the only non-zero +/// sized field contains a volatile-safe type. +fn is_struct_repr_transparent<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if let ty::Adt(adt_def, args) = ty.kind() + && adt_def.is_struct() + && adt_def.repr().transparent() + && let [fieldty] = adt_def + .all_fields() + .filter_map(|field| { + let fty = field.ty(cx.tcx, args); + if is_zero_sized_ty(cx, fty) { None } else { Some(fty) } + }) + .collect::>() + .as_slice() + { + is_volatile_safe_ty(cx, *fieldty) + } else { + false + } +} + +/// SIMD can be useful to get larger single loads/stores, though this is still +/// pretty machine-dependent. +fn is_simd_repr<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if let ty::Adt(adt_def, _args) = ty.kind() + && adt_def.is_struct() + && adt_def.repr().simd() + { + let (_size, simdty) = ty.simd_size_and_type(cx.tcx); + is_volatile_safe_ty(cx, simdty) + } else { + false + } +} + +/// Top-level predicate for whether a type is volatile-safe or not. +fn is_volatile_safe_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + ty.is_primitive() + || is_narrow_ptr(cx, ty) + || is_zero_sized_ty(cx, ty) + || is_enum_repr_c(cx, ty) + || is_simd_repr(cx, ty) + || is_struct_repr_transparent(cx, ty) + // We can't know about a generic type, so just let it pass to avoid noise + || ty.has_non_region_param() +} + +/// Print diagnostic for volatile read/write on non-volatile-safe types. +fn report_volatile_safe<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, ty: Ty<'tcx>) { + if !is_volatile_safe_ty(cx, ty) { + span_lint( + cx, + VOLATILE_COMPOSITES, + expr.span, + format!("type `{ty}` is not volatile-compatible"), + ); + } +} + +impl<'tcx> LateLintPass<'tcx> for VolatileComposites { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + // Check our expr is calling a method with pattern matching + match expr.kind { + // Look for method calls to `write_volatile`/`read_volatile`, which + // apply to both raw pointers and std::ptr::NonNull. + ExprKind::MethodCall(name, self_arg, _, _) + if matches!(name.ident.name, sym::read_volatile | sym::write_volatile) => + { + let self_ty = cx.typeck_results().expr_ty(self_arg); + match self_ty.kind() { + // Raw pointers + ty::RawPtr(innerty, _) => report_volatile_safe(cx, expr, *innerty), + // std::ptr::NonNull + ty::Adt(_, args) if is_type_diagnostic_item(cx, self_ty, sym::NonNull) => { + report_volatile_safe(cx, expr, args.type_at(0)); + }, + _ => (), + } + }, + + // Also plain function calls to std::ptr::{read,write}_volatile + ExprKind::Call(func, [arg_ptr, ..]) => { + if let ExprKind::Path(ref qpath) = func.kind + && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() + && matches!( + cx.tcx.get_diagnostic_name(def_id), + Some(sym::ptr_read_volatile | sym::ptr_write_volatile) + ) + && let ty::RawPtr(ptrty, _) = cx.typeck_results().expr_ty_adjusted(arg_ptr).kind() + { + report_volatile_safe(cx, expr, *ptrty); + } + }, + _ => {}, + } + } +} diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 4ba0e52572ddf..16858d7d32b39 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -264,6 +264,7 @@ generate! { read_to_end, read_to_string, read_unaligned, + read_volatile, redundant_imports, redundant_pub_crate, regex, @@ -373,6 +374,7 @@ generate! { wrapping_offset, write, write_unaligned, + write_volatile, writeln, zip, } diff --git a/tests/ui/volatile_composites.rs b/tests/ui/volatile_composites.rs new file mode 100644 index 0000000000000..e7e7dafe18afa --- /dev/null +++ b/tests/ui/volatile_composites.rs @@ -0,0 +1,221 @@ +#![feature(ptr_metadata)] +#![feature(portable_simd)] +#![warn(clippy::volatile_composites)] + +use std::ptr::null_mut; + +#[repr(C)] +#[derive(Copy, Clone, Default)] +struct MyDevRegisters { + baseaddr: usize, + count: usize, +} + +#[repr(transparent)] +struct Wrapper((), T, ()); + +// Not to be confused with std::ptr::NonNull +struct NonNull(T); + +impl NonNull { + fn write_volatile(&self, _arg: &T) { + unimplemented!("Something entirely unrelated to std::ptr::NonNull"); + } +} + +fn main() { + let regs = MyDevRegisters { + baseaddr: 0xabc123, + count: 42, + }; + + const DEVICE_ADDR: *mut MyDevRegisters = 0xdead as *mut _; + + // Raw pointer methods + unsafe { + (&raw mut (*DEVICE_ADDR).baseaddr).write_volatile(regs.baseaddr); // OK + (&raw mut (*DEVICE_ADDR).count).write_volatile(regs.count); // OK + + DEVICE_ADDR.write_volatile(regs); + //~^ volatile_composites + + let _regs = MyDevRegisters { + baseaddr: (&raw const (*DEVICE_ADDR).baseaddr).read_volatile(), // OK + count: (&raw const (*DEVICE_ADDR).count).read_volatile(), // OK + }; + + let _regs = DEVICE_ADDR.read_volatile(); + //~^ volatile_composites + } + + // std::ptr functions + unsafe { + std::ptr::write_volatile(&raw mut (*DEVICE_ADDR).baseaddr, regs.baseaddr); // OK + std::ptr::write_volatile(&raw mut (*DEVICE_ADDR).count, regs.count); // OK + + std::ptr::write_volatile(DEVICE_ADDR, regs); + //~^ volatile_composites + + let _regs = MyDevRegisters { + baseaddr: std::ptr::read_volatile(&raw const (*DEVICE_ADDR).baseaddr), // OK + count: std::ptr::read_volatile(&raw const (*DEVICE_ADDR).count), // OK + }; + + let _regs = std::ptr::read_volatile(DEVICE_ADDR); + //~^ volatile_composites + } + + // core::ptr functions + unsafe { + core::ptr::write_volatile(&raw mut (*DEVICE_ADDR).baseaddr, regs.baseaddr); // OK + core::ptr::write_volatile(&raw mut (*DEVICE_ADDR).count, regs.count); // OK + + core::ptr::write_volatile(DEVICE_ADDR, regs); + //~^ volatile_composites + + let _regs = MyDevRegisters { + baseaddr: core::ptr::read_volatile(&raw const (*DEVICE_ADDR).baseaddr), // OK + count: core::ptr::read_volatile(&raw const (*DEVICE_ADDR).count), // OK + }; + + let _regs = core::ptr::read_volatile(DEVICE_ADDR); + //~^ volatile_composites + } + + // std::ptr::NonNull + unsafe { + let ptr = std::ptr::NonNull::new(DEVICE_ADDR).unwrap(); + + ptr.write_volatile(regs); + //~^ volatile_composites + + let _regs = ptr.read_volatile(); + //~^ volatile_composites + } + + // Red herring + { + let thing = NonNull("hello".to_string()); + + thing.write_volatile(&"goodbye".into()); // OK + } + + // Zero size types OK + unsafe { + struct Empty; + + (0xdead as *mut Empty).write_volatile(Empty); // OK + // Note that this is OK because Wrapper is itself ZST, not because of the repr transparent + // handling tested below. + (0xdead as *mut Wrapper).write_volatile(Wrapper((), Empty, ())); // OK + } + + // Via repr transparent newtype + unsafe { + (0xdead as *mut Wrapper).write_volatile(Wrapper((), 123, ())); // OK + (0xdead as *mut Wrapper>).write_volatile(Wrapper((), Wrapper((), 123, ()), ())); // OK + + (0xdead as *mut Wrapper).write_volatile(Wrapper((), MyDevRegisters::default(), ())); + //~^ volatile_composites + } + + // Plain type alias OK + unsafe { + type MyU64 = u64; + + (0xdead as *mut MyU64).write_volatile(123); // OK + } + + // Wide pointers are not OK as data + unsafe { + let things: &[u32] = &[1, 2, 3]; + + (0xdead as *mut *const u32).write_volatile(things.as_ptr()); // OK + + let wideptr: *const [u32] = std::ptr::from_raw_parts(things.as_ptr(), things.len()); + (0xdead as *mut *const [u32]).write_volatile(wideptr); + //~^ volatile_composites + } + + // Plain pointers and pointers with lifetimes are OK + unsafe { + let v: u32 = 123; + let rv: &u32 = &v; + + (0xdead as *mut &u32).write_volatile(rv); // OK + } + + // C-style enums are OK + unsafe { + // Bad: need some specific repr + enum PlainEnum { + A = 1, + B = 2, + C = 3, + } + + (0xdead as *mut PlainEnum).write_volatile(PlainEnum::A); + //~^ volatile_composites + + // OK + #[repr(u32)] + enum U32Enum { + A = 1, + B = 2, + C = 3, + } + + (0xdead as *mut U32Enum).write_volatile(U32Enum::A); // OK + + // OK + #[repr(C)] + enum CEnum { + A = 1, + B = 2, + C = 3, + } + (0xdead as *mut CEnum).write_volatile(CEnum::A); // OK + + // Nope + enum SumType { + A(String), + B(u32), + C, + } + (0xdead as *mut SumType).write_volatile(SumType::C); + //~^ volatile_composites + + // A repr on a complex sum type is not good enough + #[repr(C)] + enum ReprSumType { + A(String), + B(u32), + C, + } + (0xdead as *mut ReprSumType).write_volatile(ReprSumType::C); + //~^ volatile_composites + } + + // SIMD is OK + unsafe { + (0xdead as *mut std::simd::u32x4).write_volatile(std::simd::u32x4::splat(1)); // OK + } + + // Can't see through generic wrapper + unsafe { + do_device_write::(0xdead as *mut _, Default::default()); // OK + } + + let mut s = String::from("foo"); + unsafe { + std::ptr::write_volatile(&mut s, String::from("bar")); + //~^ volatile_composites + } +} + +// Generic OK +unsafe fn do_device_write(ptr: *mut T, v: T) { + unsafe { + ptr.write_volatile(v); // OK + } +} diff --git a/tests/ui/volatile_composites.stderr b/tests/ui/volatile_composites.stderr new file mode 100644 index 0000000000000..1545fc913ed00 --- /dev/null +++ b/tests/ui/volatile_composites.stderr @@ -0,0 +1,89 @@ +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:39:9 + | +LL | DEVICE_ADDR.write_volatile(regs); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::volatile-composites` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::volatile_composites)]` + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:47:21 + | +LL | let _regs = DEVICE_ADDR.read_volatile(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:56:9 + | +LL | std::ptr::write_volatile(DEVICE_ADDR, regs); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:64:21 + | +LL | let _regs = std::ptr::read_volatile(DEVICE_ADDR); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:73:9 + | +LL | core::ptr::write_volatile(DEVICE_ADDR, regs); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:81:21 + | +LL | let _regs = core::ptr::read_volatile(DEVICE_ADDR); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:89:9 + | +LL | ptr.write_volatile(regs); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `MyDevRegisters` is not volatile-compatible + --> tests/ui/volatile_composites.rs:92:21 + | +LL | let _regs = ptr.read_volatile(); + | ^^^^^^^^^^^^^^^^^^^ + +error: type `Wrapper` is not volatile-compatible + --> tests/ui/volatile_composites.rs:118:9 + | +LL | (0xdead as *mut Wrapper).write_volatile(Wrapper((), MyDevRegisters::default(), ())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `*const [u32]` is not volatile-compatible + --> tests/ui/volatile_composites.rs:136:9 + | +LL | (0xdead as *mut *const [u32]).write_volatile(wideptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `main::PlainEnum` is not volatile-compatible + --> tests/ui/volatile_composites.rs:157:9 + | +LL | (0xdead as *mut PlainEnum).write_volatile(PlainEnum::A); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `main::SumType` is not volatile-compatible + --> tests/ui/volatile_composites.rs:185:9 + | +LL | (0xdead as *mut SumType).write_volatile(SumType::C); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `main::ReprSumType` is not volatile-compatible + --> tests/ui/volatile_composites.rs:195:9 + | +LL | (0xdead as *mut ReprSumType).write_volatile(ReprSumType::C); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `std::string::String` is not volatile-compatible + --> tests/ui/volatile_composites.rs:211:9 + | +LL | std::ptr::write_volatile(&mut s, String::from("bar")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 14 previous errors + From 4c128d9ee0b2aa1131c90267be8368e23708c97f Mon Sep 17 00:00:00 2001 From: Pietro Nuti Date: Mon, 21 Apr 2025 14:07:29 +0200 Subject: [PATCH 06/89] Add lint unnecessary_option_map_or_else --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/methods/mod.rs | 27 +++++ .../methods/unnecessary_option_map_or_else.rs | 73 ++++++++++++++ tests/ui/option_if_let_else.fixed | 3 +- tests/ui/option_if_let_else.rs | 3 +- tests/ui/option_if_let_else.stderr | 58 +++++------ tests/ui/or_fun_call.fixed | 1 + tests/ui/or_fun_call.rs | 1 + tests/ui/or_fun_call.stderr | 98 +++++++++---------- tests/ui/unnecessary_option_map_or_else.fixed | 47 +++++++++ tests/ui/unnecessary_option_map_or_else.rs | 54 ++++++++++ .../ui/unnecessary_option_map_or_else.stderr | 35 +++++++ 13 files changed, 322 insertions(+), 80 deletions(-) create mode 100644 clippy_lints/src/methods/unnecessary_option_map_or_else.rs create mode 100644 tests/ui/unnecessary_option_map_or_else.fixed create mode 100644 tests/ui/unnecessary_option_map_or_else.rs create mode 100644 tests/ui/unnecessary_option_map_or_else.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 30781d3d33fba..ebc2beb8d266c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6738,6 +6738,7 @@ Released 2018-09-13 [`unnecessary_min_or_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_min_or_max [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation +[`unnecessary_option_map_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_option_map_or_else [`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings [`unnecessary_result_map_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_result_map_or_else [`unnecessary_safety_comment`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_comment diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 7645ac2dd3f7b..441464378c070 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -488,6 +488,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::UNNECESSARY_LITERAL_UNWRAP_INFO, crate::methods::UNNECESSARY_MAP_OR_INFO, crate::methods::UNNECESSARY_MIN_OR_MAX_INFO, + crate::methods::UNNECESSARY_OPTION_MAP_OR_ELSE_INFO, crate::methods::UNNECESSARY_RESULT_MAP_OR_ELSE_INFO, crate::methods::UNNECESSARY_SORT_BY_INFO, crate::methods::UNNECESSARY_TO_OWNED_INFO, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a1cdab9cc491b..2e35d1a2f3eae 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -130,6 +130,7 @@ mod unnecessary_lazy_eval; mod unnecessary_literal_unwrap; mod unnecessary_map_or; mod unnecessary_min_or_max; +mod unnecessary_option_map_or_else; mod unnecessary_result_map_or_else; mod unnecessary_sort_by; mod unnecessary_to_owned; @@ -4637,6 +4638,30 @@ declare_clippy_lint! { "detects redundant calls to `Iterator::cloned`" } +declare_clippy_lint! { + /// Checks for usage of `.map_or_else()` "map closure" for `Option` type. + /// + /// ### Why is this bad? + /// This can be written more concisely by using `unwrap_or_else()`. + /// + /// ### Example + /// ```no_run + /// let k = 10; + /// let x: Option = Some(4); + /// let y = x.map_or_else(|| 2 * k, |n| n); + /// ``` + /// Use instead: + /// ```no_run + /// let k = 10; + /// let x: Option = Some(4); + /// let y = x.unwrap_or_else(|| 2 * k); + /// ``` + #[clippy::version = "1.88.0"] + pub UNNECESSARY_OPTION_MAP_OR_ELSE, + suspicious, + "making no use of the \"map closure\" when calling `.map_or_else(|| 2 * k, |n| n)`" +} + #[expect(clippy::struct_excessive_bools)] pub struct Methods { avoid_breaking_exported_api: bool, @@ -4818,6 +4843,7 @@ impl_lint_pass!(Methods => [ SWAP_WITH_TEMPORARY, IP_CONSTANT, REDUNDANT_ITER_CLONED, + UNNECESSARY_OPTION_MAP_OR_ELSE, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -5354,6 +5380,7 @@ impl Methods { }, (sym::map_or_else, [def, map]) => { result_map_or_else_none::check(cx, expr, recv, def, map); + unnecessary_option_map_or_else::check(cx, expr, recv, def, map); unnecessary_result_map_or_else::check(cx, expr, recv, def, map); }, (sym::next, []) => { diff --git a/clippy_lints/src/methods/unnecessary_option_map_or_else.rs b/clippy_lints/src/methods/unnecessary_option_map_or_else.rs new file mode 100644 index 0000000000000..ec709e1ab5cdc --- /dev/null +++ b/clippy_lints/src/methods/unnecessary_option_map_or_else.rs @@ -0,0 +1,73 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::peel_blocks; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath}; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; + +use super::UNNECESSARY_OPTION_MAP_OR_ELSE; +use super::utils::get_last_chain_binding_hir_id; + +fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>) { + let msg = "unused \"map closure\" when calling `Option::map_or_else` value"; + let self_snippet = snippet(cx, recv.span, ".."); + let err_snippet = snippet(cx, def_arg.span, ".."); + span_lint_and_sugg( + cx, + UNNECESSARY_OPTION_MAP_OR_ELSE, + expr.span, + msg, + "consider using `unwrap_or_else`", + format!("{self_snippet}.unwrap_or_else({err_snippet})"), + Applicability::MachineApplicable, + ); +} + +fn handle_qpath( + cx: &LateContext<'_>, + expr: &Expr<'_>, + recv: &Expr<'_>, + def_arg: &Expr<'_>, + expected_hir_id: HirId, + qpath: QPath<'_>, +) { + if let QPath::Resolved(_, path) = qpath + && let rustc_hir::def::Res::Local(hir_id) = path.res + && expected_hir_id == hir_id + { + emit_lint(cx, expr, recv, def_arg); + } +} + +/// lint use of `_.map_or_else(|err| err, |n| n)` for `Option`s. +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, map_arg: &Expr<'_>) { + // lint if the caller of `map_or_else()` is an `Option` + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option) + && let ExprKind::Closure(&Closure { body, .. }) = map_arg.kind + && let body = cx.tcx.hir_body(body) + && let Some(first_param) = body.params.first() + { + let body_expr = peel_blocks(body.value); + + match body_expr.kind { + ExprKind::Path(qpath) => { + handle_qpath(cx, expr, recv, def_arg, first_param.pat.hir_id, qpath); + }, + // If this is a block (that wasn't peeled off), then it means there are statements. + ExprKind::Block(block, _) => { + if let Some(block_expr) = block.expr + // First we ensure that this is a "binding chain" (each statement is a binding + // of the previous one) and that it is a binding of the closure argument. + && let Some(last_chain_binding_id) = + get_last_chain_binding_hir_id(first_param.pat.hir_id, block.stmts) + && let ExprKind::Path(qpath) = block_expr.kind + { + handle_qpath(cx, expr, recv, def_arg, last_chain_binding_id, qpath); + } + }, + _ => {}, + } + } +} diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 6ce067f5c2462..a2ecea773efe5 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -6,7 +6,8 @@ clippy::redundant_locals, clippy::manual_midpoint, clippy::manual_unwrap_or_default, - clippy::manual_unwrap_or + clippy::manual_unwrap_or, + clippy::unnecessary_option_map_or_else )] 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 096d3aabf28db..3adbc785237f6 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -6,7 +6,8 @@ clippy::redundant_locals, clippy::manual_midpoint, clippy::manual_unwrap_or_default, - clippy::manual_unwrap_or + clippy::manual_unwrap_or, + clippy::unnecessary_option_map_or_else )] 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 21a80ae038d8c..f5578f63c946b 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:13:5 + --> tests/ui/option_if_let_else.rs:14:5 | LL | / if let Some(x) = string { LL | | @@ -13,19 +13,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:32:13 + --> tests/ui/option_if_let_else.rs:33: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:34:13 + --> tests/ui/option_if_let_else.rs:35: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:36:13 + --> tests/ui/option_if_let_else.rs:37:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -47,13 +47,13 @@ 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 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:45:13 + --> tests/ui/option_if_let_else.rs:46:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -75,7 +75,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:52:13 + --> tests/ui/option_if_let_else.rs:53:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -97,7 +97,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:62:5 + --> tests/ui/option_if_let_else.rs:63:5 | LL | / if let Some(x) = arg { LL | | @@ -118,7 +118,7 @@ LL + }) | error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:76:13 + --> tests/ui/option_if_let_else.rs:77:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -131,7 +131,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:86:13 + --> tests/ui/option_if_let_else.rs:87:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -154,7 +154,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:120:13 + --> tests/ui/option_if_let_else.rs:121:13 | LL | / if let Some(idx) = s.find('.') { LL | | @@ -165,7 +165,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:132:5 + --> tests/ui/option_if_let_else.rs:133:5 | LL | / if let Ok(binding) = variable { LL | | @@ -189,7 +189,7 @@ LL + }) | error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:157:5 + --> tests/ui/option_if_let_else.rs:158:5 | LL | / match r { LL | | @@ -199,7 +199,7 @@ LL | | } | |_____^ help: try: `r.map_or_else(|_| Vec::new(), |s| s.to_owned())` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:166:5 + --> tests/ui/option_if_let_else.rs:167:5 | LL | / if let Ok(s) = r { s.to_owned() } LL | | @@ -207,13 +207,13 @@ LL | | else { Vec::new() } | |_______________________^ help: try: `r.map_or_else(|_| Vec::new(), |s| s.to_owned())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:173:13 + --> tests/ui/option_if_let_else.rs:174: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:184:13 + --> tests/ui/option_if_let_else.rs:185:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -235,13 +235,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:213:13 + --> tests/ui/option_if_let_else.rs:214: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:218:13 + --> tests/ui/option_if_let_else.rs:219:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -263,7 +263,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:258:13 + --> tests/ui/option_if_let_else.rs:259:13 | LL | let _ = match s { | _____________^ @@ -274,7 +274,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:263:13 + --> tests/ui/option_if_let_else.rs:264:13 | LL | let _ = match Some(10) { | _____________^ @@ -285,7 +285,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:270:13 + --> tests/ui/option_if_let_else.rs:271:13 | LL | let _ = match res { | _____________^ @@ -296,7 +296,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:275:13 + --> tests/ui/option_if_let_else.rs:276:13 | LL | let _ = match res { | _____________^ @@ -307,13 +307,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:280:13 + --> tests/ui/option_if_let_else.rs:281: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:298:17 + --> tests/ui/option_if_let_else.rs:299:17 | LL | let _ = match initial { | _________________^ @@ -324,7 +324,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:306:17 + --> tests/ui/option_if_let_else.rs:307:17 | LL | let _ = match initial { | _________________^ @@ -335,7 +335,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:330:24 + --> tests/ui/option_if_let_else.rs:331:24 | LL | let mut _hashmap = if let Some(hm) = &opt { | ________________________^ @@ -347,19 +347,19 @@ 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:337:19 + --> tests/ui/option_if_let_else.rs:338: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())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:388:22 + --> tests/ui/option_if_let_else.rs:389:22 | LL | let _ = unsafe { if let Some(o) = *opt_raw_ptr { o } else { 1 } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*opt_raw_ptr).map_or(1, |o| o)` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:394:13 + --> tests/ui/option_if_let_else.rs:395:13 | LL | let _ = match res { | _____________^ diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 386351aa39f51..314da0804a5fa 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -5,6 +5,7 @@ clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap, clippy::unnecessary_result_map_or_else, + clippy::unnecessary_option_map_or_else, clippy::useless_vec )] diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index e27f9aa65c37a..2a19614026ec5 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -5,6 +5,7 @@ clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap, clippy::unnecessary_result_map_or_else, + clippy::unnecessary_option_map_or_else, clippy::useless_vec )] diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 6bce06ab20eb6..3d55f2cd1f9fc 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -1,5 +1,5 @@ error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:52:22 + --> tests/ui/or_fun_call.rs:53:22 | LL | with_constructor.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(make)` @@ -8,7 +8,7 @@ LL | with_constructor.unwrap_or(make()); = help: to override `-D warnings` add `#[allow(clippy::or_fun_call)]` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:56:14 + --> tests/ui/or_fun_call.rs:57:14 | LL | with_new.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` @@ -17,205 +17,205 @@ LL | with_new.unwrap_or(Vec::new()); = help: to override `-D warnings` add `#[allow(clippy::unwrap_or_default)]` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:60:21 + --> tests/ui/or_fun_call.rs:61:21 | LL | with_const_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Vec::with_capacity(12))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:64:14 + --> tests/ui/or_fun_call.rs:65:14 | LL | with_err.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| make())` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:68:19 + --> tests/ui/or_fun_call.rs:69:19 | LL | with_err_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Vec::with_capacity(12))` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:72:24 + --> tests/ui/or_fun_call.rs:73:24 | LL | with_default_trait.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:76:23 + --> tests/ui/or_fun_call.rs:77:23 | LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:96:18 + --> tests/ui/or_fun_call.rs:97:18 | LL | self_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(::default)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:100:18 + --> tests/ui/or_fun_call.rs:101:18 | LL | real_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:104:14 + --> tests/ui/or_fun_call.rs:105:14 | LL | with_vec.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:108:21 + --> tests/ui/or_fun_call.rs:109:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(Foo::new)` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:112:19 + --> tests/ui/or_fun_call.rs:113:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:116:23 + --> tests/ui/or_fun_call.rs:117:23 | LL | map_vec.entry(42).or_insert(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:120:21 + --> tests/ui/or_fun_call.rs:121:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:124:25 + --> tests/ui/or_fun_call.rs:125:25 | LL | btree_vec.entry(42).or_insert(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:128:21 + --> tests/ui/or_fun_call.rs:129:21 | LL | let _ = stringy.unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `ok_or` - --> tests/ui/or_fun_call.rs:133:17 + --> tests/ui/or_fun_call.rs:134:17 | LL | let _ = opt.ok_or(format!("{} world.", hello)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:138:21 + --> tests/ui/or_fun_call.rs:139:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:141:21 + --> tests/ui/or_fun_call.rs:142:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `or` - --> tests/ui/or_fun_call.rs:166:35 + --> tests/ui/or_fun_call.rs:167:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:209:18 + --> tests/ui/or_fun_call.rs:210:18 | LL | None.unwrap_or(ptr_to_ref(s)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:217:14 + --> tests/ui/or_fun_call.rs:218:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:220:14 + --> tests/ui/or_fun_call.rs:221:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:296:25 + --> tests/ui/or_fun_call.rs:297:25 | LL | let _ = Some(4).map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:298:25 + --> tests/ui/or_fun_call.rs:299:25 | LL | let _ = Some(4).map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:301:25 + --> tests/ui/or_fun_call.rs:302:25 | LL | let _ = Some(4).map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| "asd".to_string().len() as i32, f)` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:332:18 + --> tests/ui/or_fun_call.rs:333:18 | LL | with_new.unwrap_or_else(Vec::new); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:336:28 + --> tests/ui/or_fun_call.rs:337:28 | LL | with_default_trait.unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:340:27 + --> tests/ui/or_fun_call.rs:341:27 | LL | with_default_type.unwrap_or_else(u64::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:344:22 + --> tests/ui/or_fun_call.rs:345:22 | LL | real_default.unwrap_or_else(::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:348:23 + --> tests/ui/or_fun_call.rs:349:23 | LL | map.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:352:25 + --> tests/ui/or_fun_call.rs:353:25 | LL | btree.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:356:25 + --> tests/ui/or_fun_call.rs:357:25 | LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:398:17 + --> tests/ui/or_fun_call.rs:399:17 | LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:403:17 + --> tests/ui/or_fun_call.rs:404:17 | LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:408:17 + --> tests/ui/or_fun_call.rs:409:17 | LL | let _ = opt.unwrap_or({ | _________________^ @@ -235,79 +235,79 @@ LL ~ }); | error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:414:17 + --> tests/ui/or_fun_call.rs:415:17 | LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:419:17 + --> tests/ui/or_fun_call.rs:420:17 | LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:426:21 + --> tests/ui/or_fun_call.rs:427:21 | LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:441:19 + --> tests/ui/or_fun_call.rs:442:19 | LL | let _ = x.map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:443:19 + --> tests/ui/or_fun_call.rs:444:19 | LL | let _ = x.map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:446:19 + --> tests/ui/or_fun_call.rs:447:19 | LL | let _ = x.map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| "asd".to_string().len() as i32, f)` error: function call inside of `get_or_insert` - --> tests/ui/or_fun_call.rs:457:15 + --> tests/ui/or_fun_call.rs:458:15 | LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:467:15 + --> tests/ui/or_fun_call.rs:468:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:477:15 + --> tests/ui/or_fun_call.rs:478:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:483:17 + --> tests/ui/or_fun_call.rs:484:17 | LL | let _ = opt.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:485:17 + --> tests/ui/or_fun_call.rs:486:17 | LL | let _ = res.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Default::default())` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:491:17 + --> tests/ui/or_fun_call.rs:492:17 | LL | let _ = opt.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:493:17 + --> tests/ui/or_fun_call.rs:494:17 | LL | let _ = res.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` diff --git a/tests/ui/unnecessary_option_map_or_else.fixed b/tests/ui/unnecessary_option_map_or_else.fixed new file mode 100644 index 0000000000000..5f0039f9fea5f --- /dev/null +++ b/tests/ui/unnecessary_option_map_or_else.fixed @@ -0,0 +1,47 @@ +#![warn(clippy::unnecessary_option_map_or_else)] +#![allow( + clippy::let_and_return, + clippy::let_unit_value, + clippy::unnecessary_lazy_evaluations, + clippy::unnecessary_literal_unwrap +)] + +fn main() { + // Expected errors + // Basic scenario + let option = Some(()); + option.unwrap_or_else(|| ()); //~ ERROR: unused "map closure" when calling + + // Type ascription + let option = Some(()); + option.unwrap_or_else(|| ()); //~ ERROR: unused "map closure" when calling + + // Auto-deref + let string = String::new(); + let option = Some(&string); + let _: &str = option.unwrap_or_else(|| &string); //~ ERROR: unused "map closure" when calling + + // Temporary variable + let option = Some(()); + option.unwrap_or_else(|| ()); + + // Correct usages + let option = Some(()); + option.map_or_else(|| (), |_| ()); + + let option = Some(()); + option.map_or_else(|| (), |_: ()| ()); + + let string = String::new(); + let option = Some(&string); + let _: &str = option.map_or_else(|| &string, |_| &string); + + let option = Some(()); + option.map_or_else( + || (), + |_| { + let tmp = (); + tmp + }, + ); +} diff --git a/tests/ui/unnecessary_option_map_or_else.rs b/tests/ui/unnecessary_option_map_or_else.rs new file mode 100644 index 0000000000000..a748b33018148 --- /dev/null +++ b/tests/ui/unnecessary_option_map_or_else.rs @@ -0,0 +1,54 @@ +#![warn(clippy::unnecessary_option_map_or_else)] +#![allow( + clippy::let_and_return, + clippy::let_unit_value, + clippy::unnecessary_lazy_evaluations, + clippy::unnecessary_literal_unwrap +)] + +fn main() { + // Expected errors + // Basic scenario + let option = Some(()); + option.map_or_else(|| (), |x| x); //~ ERROR: unused "map closure" when calling + + // Type ascription + let option = Some(()); + option.map_or_else(|| (), |x: ()| x); //~ ERROR: unused "map closure" when calling + + // Auto-deref + let string = String::new(); + let option = Some(&string); + let _: &str = option.map_or_else(|| &string, |x| x); //~ ERROR: unused "map closure" when calling + + // Temporary variable + let option = Some(()); + option.map_or_else( + //~^ ERROR: unused "map closure" when calling + || (), + |x| { + let tmp = x; + tmp + }, + ); + + // Correct usages + let option = Some(()); + option.map_or_else(|| (), |_| ()); + + let option = Some(()); + option.map_or_else(|| (), |_: ()| ()); + + let string = String::new(); + let option = Some(&string); + let _: &str = option.map_or_else(|| &string, |_| &string); + + let option = Some(()); + option.map_or_else( + || (), + |_| { + let tmp = (); + tmp + }, + ); +} diff --git a/tests/ui/unnecessary_option_map_or_else.stderr b/tests/ui/unnecessary_option_map_or_else.stderr new file mode 100644 index 0000000000000..3fc4cdc73d0f3 --- /dev/null +++ b/tests/ui/unnecessary_option_map_or_else.stderr @@ -0,0 +1,35 @@ +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:13:5 + | +LL | option.map_or_else(|| (), |x| x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` + | + = note: `-D clippy::unnecessary-option-map-or-else` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_option_map_or_else)]` + +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:17:5 + | +LL | option.map_or_else(|| (), |x: ()| x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` + +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:22:19 + | +LL | let _: &str = option.map_or_else(|| &string, |x| x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| &string)` + +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:26:5 + | +LL | / option.map_or_else( +LL | | +LL | | || (), +LL | | |x| { +... | +LL | | }, +LL | | ); + | |_____^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` + +error: aborting due to 4 previous errors + From e410d6f09e42555b2d537c32d99b10cab79e59ac Mon Sep 17 00:00:00 2001 From: Pietro Nuti Date: Sun, 13 Jul 2025 13:39:17 +0200 Subject: [PATCH 07/89] Add test cases for closure binding and function identity --- .../methods/unnecessary_option_map_or_else.rs | 68 +++++++++++++++---- tests/ui/unnecessary_option_map_or_else.fixed | 28 ++++++++ tests/ui/unnecessary_option_map_or_else.rs | 28 ++++++++ .../ui/unnecessary_option_map_or_else.stderr | 22 ++++-- 4 files changed, 126 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_option_map_or_else.rs b/clippy_lints/src/methods/unnecessary_option_map_or_else.rs index ec709e1ab5cdc..35b72fe8fc194 100644 --- a/clippy_lints/src/methods/unnecessary_option_map_or_else.rs +++ b/clippy_lints/src/methods/unnecessary_option_map_or_else.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::peel_blocks; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{expr_or_init, find_binding_init, peel_blocks}; use rustc_errors::Applicability; -use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Body, BodyId, Closure, Expr, ExprKind, HirId, QPath}; use rustc_lint::LateContext; use rustc_span::symbol::sym; @@ -12,8 +13,9 @@ use super::utils::get_last_chain_binding_hir_id; fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>) { let msg = "unused \"map closure\" when calling `Option::map_or_else` value"; - let self_snippet = snippet(cx, recv.span, ".."); - let err_snippet = snippet(cx, def_arg.span, ".."); + let mut applicability = Applicability::MachineApplicable; + let self_snippet = snippet_with_applicability(cx, recv.span, "_", &mut applicability); + let err_snippet = snippet_with_applicability(cx, def_arg.span, "..", &mut applicability); span_lint_and_sugg( cx, UNNECESSARY_OPTION_MAP_OR_ELSE, @@ -34,23 +36,21 @@ fn handle_qpath( qpath: QPath<'_>, ) { if let QPath::Resolved(_, path) = qpath - && let rustc_hir::def::Res::Local(hir_id) = path.res + && let Res::Local(hir_id) = path.res && expected_hir_id == hir_id { emit_lint(cx, expr, recv, def_arg); } } -/// lint use of `_.map_or_else(|err| err, |n| n)` for `Option`s. -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, map_arg: &Expr<'_>) { - // lint if the caller of `map_or_else()` is an `Option` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option) - && let ExprKind::Closure(&Closure { body, .. }) = map_arg.kind - && let body = cx.tcx.hir_body(body) - && let Some(first_param) = body.params.first() - { - let body_expr = peel_blocks(body.value); +fn handle_closure(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, body_id: BodyId) { + let body = cx.tcx.hir_body(body_id); + handle_fn_body(cx, expr, recv, def_arg, body); +} +fn handle_fn_body(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, body: &Body<'_>) { + if let Some(first_param) = body.params.first() { + let body_expr = peel_blocks(body.value); match body_expr.kind { ExprKind::Path(qpath) => { handle_qpath(cx, expr, recv, def_arg, first_param.pat.hir_id, qpath); @@ -71,3 +71,41 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_ } } } + +/// lint use of `_.map_or_else(|err| err, |n| n)` for `Option`s. +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, map_arg: &Expr<'_>) { + // lint if the caller of `map_or_else()` is an `Option` + if !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option) { + return; + } + match map_arg.kind { + // If the second argument is a closure, we can check its body. + ExprKind::Closure(&Closure { body, .. }) => { + handle_closure(cx, expr, recv, def_arg, body); + }, + ExprKind::Path(qpath) => { + let res = cx.qpath_res(&qpath, map_arg.hir_id); + match res { + // Case 1: Local variable (could be a closure) + Res::Local(hir_id) => { + if let Some(init_expr) = find_binding_init(cx, hir_id) { + let origin = expr_or_init(cx, init_expr); + if let ExprKind::Closure(&Closure { body, .. }) = origin.kind { + handle_closure(cx, expr, recv, def_arg, body); + } + } + }, + // Case 2: Function definition + Res::Def(DefKind::Fn, def_id) => { + if let Some(local_def_id) = def_id.as_local() + && let Some(body) = cx.tcx.hir_maybe_body_owned_by(local_def_id) + { + handle_fn_body(cx, expr, recv, def_arg, body); + } + }, + _ => (), + } + }, + _ => (), + } +} diff --git a/tests/ui/unnecessary_option_map_or_else.fixed b/tests/ui/unnecessary_option_map_or_else.fixed index 5f0039f9fea5f..9974ee2d08eb5 100644 --- a/tests/ui/unnecessary_option_map_or_else.fixed +++ b/tests/ui/unnecessary_option_map_or_else.fixed @@ -6,6 +6,14 @@ clippy::unnecessary_literal_unwrap )] +const fn identity(x: T) -> T { + x +} + +const fn double_it(x: i32) -> i32 { + x * 2 +} + fn main() { // Expected errors // Basic scenario @@ -25,6 +33,17 @@ fn main() { let option = Some(()); option.unwrap_or_else(|| ()); + // Identity + let string = String::new(); + let option = Some(&string); + let _: &str = option.unwrap_or_else(|| &string); //~ ERROR: unused "map closure" when calling + + // Closure bound to a variable + let do_nothing = |x: String| x; + let string = String::new(); + let option = Some(string.clone()); + let _: String = option.unwrap_or_else(|| string); //~ ERROR: unused "map closure" when calling + // Correct usages let option = Some(()); option.map_or_else(|| (), |_| ()); @@ -44,4 +63,13 @@ fn main() { tmp }, ); + + let num = 5; + let option = Some(num); + let _: i32 = option.map_or_else(|| 0, double_it); + + let increase = |x: i32| x + 1; + let num = 5; + let option = Some(num); + let _: i32 = option.map_or_else(|| 0, increase); } diff --git a/tests/ui/unnecessary_option_map_or_else.rs b/tests/ui/unnecessary_option_map_or_else.rs index a748b33018148..9b53f3fcd521c 100644 --- a/tests/ui/unnecessary_option_map_or_else.rs +++ b/tests/ui/unnecessary_option_map_or_else.rs @@ -6,6 +6,14 @@ clippy::unnecessary_literal_unwrap )] +const fn identity(x: T) -> T { + x +} + +const fn double_it(x: i32) -> i32 { + x * 2 +} + fn main() { // Expected errors // Basic scenario @@ -32,6 +40,17 @@ fn main() { }, ); + // Identity + let string = String::new(); + let option = Some(&string); + let _: &str = option.map_or_else(|| &string, identity); //~ ERROR: unused "map closure" when calling + + // Closure bound to a variable + let do_nothing = |x: String| x; + let string = String::new(); + let option = Some(string.clone()); + let _: String = option.map_or_else(|| string, do_nothing); //~ ERROR: unused "map closure" when calling + // Correct usages let option = Some(()); option.map_or_else(|| (), |_| ()); @@ -51,4 +70,13 @@ fn main() { tmp }, ); + + let num = 5; + let option = Some(num); + let _: i32 = option.map_or_else(|| 0, double_it); + + let increase = |x: i32| x + 1; + let num = 5; + let option = Some(num); + let _: i32 = option.map_or_else(|| 0, increase); } diff --git a/tests/ui/unnecessary_option_map_or_else.stderr b/tests/ui/unnecessary_option_map_or_else.stderr index 3fc4cdc73d0f3..d90875e4efc7c 100644 --- a/tests/ui/unnecessary_option_map_or_else.stderr +++ b/tests/ui/unnecessary_option_map_or_else.stderr @@ -1,5 +1,5 @@ error: unused "map closure" when calling `Option::map_or_else` value - --> tests/ui/unnecessary_option_map_or_else.rs:13:5 + --> tests/ui/unnecessary_option_map_or_else.rs:21:5 | LL | option.map_or_else(|| (), |x| x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` @@ -8,19 +8,19 @@ LL | option.map_or_else(|| (), |x| x); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_option_map_or_else)]` error: unused "map closure" when calling `Option::map_or_else` value - --> tests/ui/unnecessary_option_map_or_else.rs:17:5 + --> tests/ui/unnecessary_option_map_or_else.rs:25:5 | LL | option.map_or_else(|| (), |x: ()| x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` error: unused "map closure" when calling `Option::map_or_else` value - --> tests/ui/unnecessary_option_map_or_else.rs:22:19 + --> tests/ui/unnecessary_option_map_or_else.rs:30:19 | LL | let _: &str = option.map_or_else(|| &string, |x| x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| &string)` error: unused "map closure" when calling `Option::map_or_else` value - --> tests/ui/unnecessary_option_map_or_else.rs:26:5 + --> tests/ui/unnecessary_option_map_or_else.rs:34:5 | LL | / option.map_or_else( LL | | @@ -31,5 +31,17 @@ LL | | }, LL | | ); | |_____^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` -error: aborting due to 4 previous errors +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:46:19 + | +LL | let _: &str = option.map_or_else(|| &string, identity); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| &string)` + +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:52:21 + | +LL | let _: String = option.map_or_else(|| string, do_nothing); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| string)` + +error: aborting due to 6 previous errors From 6d06db51b6f487bcafcf58554bf471b90fb529c6 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Fri, 30 May 2025 16:22:56 -0600 Subject: [PATCH 08/89] test: Subtract code_offset from width for ui_testing --- tests/ui/option_env_unwrap.stderr | 2 +- tests/ui/too_long_first_doc_paragraph.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/option_env_unwrap.stderr b/tests/ui/option_env_unwrap.stderr index bbcbfedb78822..c14a3ea23ff3e 100644 --- a/tests/ui/option_env_unwrap.stderr +++ b/tests/ui/option_env_unwrap.stderr @@ -19,7 +19,7 @@ LL | let _ = option_env!("PATH").expect("environment variable PATH isn't set error: this will panic at run-time if the environment variable doesn't exist at compile-time --> tests/ui/option_env_unwrap.rs:14:13 | -LL | let _ = option_env!("__Y__do_not_use").unwrap(); // This test only works if you don't have a __Y__do_not_use env variable in your env... +LL | let _ = option_env!("__Y__do_not_use").unwrap(); // This test only works if you don't have a __Y__do_not_use env variable in you... | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using the `env!` macro instead diff --git a/tests/ui/too_long_first_doc_paragraph.stderr b/tests/ui/too_long_first_doc_paragraph.stderr index f3f182aa54222..949ada30dc97d 100644 --- a/tests/ui/too_long_first_doc_paragraph.stderr +++ b/tests/ui/too_long_first_doc_paragraph.stderr @@ -42,7 +42,7 @@ LL | | /// gravida non lacinia at, rhoncus eu lacus. error: first doc comment paragraph is too long --> tests/ui/too_long_first_doc_paragraph.rs:65:1 | -LL | / /// Some function. This doc-string paragraph is too long. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lore... +LL | / /// Some function. This doc-string paragraph is too long. Lorem Ipsum is simply dummy text of the printing and typesetting industr... LL | | LL | | /// LL | | /// Here's a second paragraph. It would be preferable to put the details here. From 434fe184e6f65d993d810714275935dcc1b56eea Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 4 Oct 2025 01:16:59 +0200 Subject: [PATCH 09/89] fix(zero_repeat_side_effects): don't suggest unsuggestable types --- clippy_lints/src/zero_repeat_side_effects.rs | 23 +++++++++++++------ ...ro_repeat_side_effects_never_pattern.fixed | 9 ++++++++ .../zero_repeat_side_effects_never_pattern.rs | 9 ++++++++ ...o_repeat_side_effects_never_pattern.stderr | 16 +++++++++++++ .../ui/zero_repeat_side_effects_unfixable.rs | 13 +++++++++++ .../zero_repeat_side_effects_unfixable.stderr | 20 ++++++++++++++++ 6 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 tests/ui/zero_repeat_side_effects_never_pattern.fixed create mode 100644 tests/ui/zero_repeat_side_effects_never_pattern.rs create mode 100644 tests/ui/zero_repeat_side_effects_never_pattern.stderr create mode 100644 tests/ui/zero_repeat_side_effects_unfixable.rs create mode 100644 tests/ui/zero_repeat_side_effects_unfixable.stderr diff --git a/clippy_lints/src/zero_repeat_side_effects.rs b/clippy_lints/src/zero_repeat_side_effects.rs index cd6c11b512742..db54ec023077a 100644 --- a/clippy_lints/src/zero_repeat_side_effects.rs +++ b/clippy_lints/src/zero_repeat_side_effects.rs @@ -6,6 +6,7 @@ use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{ConstArgKind, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::IsSuggestable; use rustc_session::declare_lint_pass; declare_clippy_lint! { @@ -72,6 +73,7 @@ fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: // check if expr is a call or has a call inside it if inner_expr.can_have_side_effects() { let parent_hir_node = cx.tcx.parent_hir_node(expr.hir_id); + let inner_expr_ty = cx.typeck_results().expr_ty(inner_expr); let return_type = cx.typeck_results().expr_ty(expr); let inner_expr = snippet(cx, inner_expr.span.source_callsite(), ".."); @@ -94,18 +96,25 @@ fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: ), _ => (expr.span, format!("{{ {inner_expr}; {vec}[] as {return_type} }}")), }; + let span = span.source_callsite(); span_lint_and_then( cx, ZERO_REPEAT_SIDE_EFFECTS, - span.source_callsite(), + span, "expression with side effects as the initial value in a zero-sized array initializer", |diag| { - diag.span_suggestion_verbose( - span.source_callsite(), - "consider performing the side effect separately", - sugg, - Applicability::Unspecified, - ); + if (!inner_expr_ty.is_never() || cx.tcx.features().never_type()) + && return_type.is_suggestable(cx.tcx, true) + { + diag.span_suggestion_verbose( + span, + "consider performing the side effect separately", + sugg, + Applicability::Unspecified, + ); + } else { + diag.help("consider performing the side effect separately"); + } }, ); } diff --git a/tests/ui/zero_repeat_side_effects_never_pattern.fixed b/tests/ui/zero_repeat_side_effects_never_pattern.fixed new file mode 100644 index 0000000000000..021265dac9844 --- /dev/null +++ b/tests/ui/zero_repeat_side_effects_never_pattern.fixed @@ -0,0 +1,9 @@ +#![warn(clippy::zero_repeat_side_effects)] +#![allow(clippy::diverging_sub_expression)] +#![feature(never_type)] + +fn issue_14998() { + // nameable type thanks to `never_type` being enabled, suggest + panic!(); let _data: [!; 0] = []; + //~^ zero_repeat_side_effects +} diff --git a/tests/ui/zero_repeat_side_effects_never_pattern.rs b/tests/ui/zero_repeat_side_effects_never_pattern.rs new file mode 100644 index 0000000000000..3dc1929bcdc76 --- /dev/null +++ b/tests/ui/zero_repeat_side_effects_never_pattern.rs @@ -0,0 +1,9 @@ +#![warn(clippy::zero_repeat_side_effects)] +#![allow(clippy::diverging_sub_expression)] +#![feature(never_type)] + +fn issue_14998() { + // nameable type thanks to `never_type` being enabled, suggest + let _data = [panic!(); 0]; + //~^ zero_repeat_side_effects +} diff --git a/tests/ui/zero_repeat_side_effects_never_pattern.stderr b/tests/ui/zero_repeat_side_effects_never_pattern.stderr new file mode 100644 index 0000000000000..b3d3d2f88f541 --- /dev/null +++ b/tests/ui/zero_repeat_side_effects_never_pattern.stderr @@ -0,0 +1,16 @@ +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects_never_pattern.rs:7:5 + | +LL | let _data = [panic!(); 0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::zero-repeat-side-effects` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::zero_repeat_side_effects)]` +help: consider performing the side effect separately + | +LL - let _data = [panic!(); 0]; +LL + panic!(); let _data: [!; 0] = []; + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/zero_repeat_side_effects_unfixable.rs b/tests/ui/zero_repeat_side_effects_unfixable.rs new file mode 100644 index 0000000000000..82f0884056ab0 --- /dev/null +++ b/tests/ui/zero_repeat_side_effects_unfixable.rs @@ -0,0 +1,13 @@ +//@no-rustfix +#![warn(clippy::zero_repeat_side_effects)] +#![expect(clippy::diverging_sub_expression)] + +fn issue_14998() { + // unnameable types, don't suggest + let _data = [|| 3i32; 0]; + //~^ zero_repeat_side_effects + + // unnameable type because `never_type` is not enabled, don't suggest + let _data = [panic!(); 0]; + //~^ zero_repeat_side_effects +} diff --git a/tests/ui/zero_repeat_side_effects_unfixable.stderr b/tests/ui/zero_repeat_side_effects_unfixable.stderr new file mode 100644 index 0000000000000..450617f3782c4 --- /dev/null +++ b/tests/ui/zero_repeat_side_effects_unfixable.stderr @@ -0,0 +1,20 @@ +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects_unfixable.rs:7:5 + | +LL | let _data = [|| 3i32; 0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider performing the side effect separately + = note: `-D clippy::zero-repeat-side-effects` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::zero_repeat_side_effects)]` + +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects_unfixable.rs:11:5 + | +LL | let _data = [panic!(); 0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider performing the side effect separately + +error: aborting due to 2 previous errors + From 8e3315f2926a8a0b2156ac99ff397d07dbecfee0 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 3 Aug 2025 17:58:39 +0200 Subject: [PATCH 10/89] misc: extend let-chain --- clippy_lints/src/manual_rotate.rs | 49 +++++++++++++------------------ 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/manual_rotate.rs b/clippy_lints/src/manual_rotate.rs index 22e3407303f07..c456920b198d4 100644 --- a/clippy_lints/src/manual_rotate.rs +++ b/clippy_lints/src/manual_rotate.rs @@ -80,38 +80,31 @@ impl LateLintPass<'_> for ManualRotate { && let BinOpKind::Add | BinOpKind::BitOr = op.node && let Some((l_shift_dir, l_amount, l_expr)) = parse_shift(cx, l) && let Some((r_shift_dir, r_amount, r_expr)) = parse_shift(cx, r) - { - if l_shift_dir == r_shift_dir { - return; - } - if !clippy_utils::eq_expr_value(cx, l_expr, r_expr) { - return; - } - let Some(bit_width) = (match cx.typeck_results().expr_ty(expr).kind() { + && l_shift_dir != r_shift_dir + && clippy_utils::eq_expr_value(cx, l_expr, r_expr) + && let Some(bit_width) = match cx.typeck_results().expr_ty(expr).kind() { ty::Int(itype) => itype.bit_width(), ty::Uint(itype) => itype.bit_width(), _ => return, - }) else { - return; - }; - if l_amount + r_amount == u128::from(bit_width) { - let (shift_function, amount) = if l_amount < r_amount { - (l_shift_dir, l_amount) - } else { - (r_shift_dir, r_amount) - }; - let mut applicability = Applicability::MachineApplicable; - let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); - span_lint_and_sugg( - cx, - MANUAL_ROTATE, - expr.span, - "there is no need to manually implement bit rotation", - "this expression can be rewritten as", - format!("{expr_sugg}.{shift_function}({amount})"), - Applicability::MachineApplicable, - ); } + && l_amount + r_amount == u128::from(bit_width) + { + let (shift_function, amount) = if l_amount < r_amount { + (l_shift_dir, l_amount) + } else { + (r_shift_dir, r_amount) + }; + let mut applicability = Applicability::MachineApplicable; + let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); + span_lint_and_sugg( + cx, + MANUAL_ROTATE, + expr.span, + "there is no need to manually implement bit rotation", + "this expression can be rewritten as", + format!("{expr_sugg}.{shift_function}({amount})"), + Applicability::MachineApplicable, + ); } } } From c9ee445abd0427f0117a7ea183459dddbad2731b Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 3 Aug 2025 18:16:02 +0200 Subject: [PATCH 11/89] extract the constant codepath switch around `*_amount` and `*_expr` -- otherwise the order gets confusing now because of both of them being `Expr`s --- clippy_lints/src/manual_rotate.rs | 55 ++++++++++++++++--------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/manual_rotate.rs b/clippy_lints/src/manual_rotate.rs index c456920b198d4..d531746830f64 100644 --- a/clippy_lints/src/manual_rotate.rs +++ b/clippy_lints/src/manual_rotate.rs @@ -56,20 +56,14 @@ impl Display for ShiftDirection { } } -fn parse_shift<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, -) -> Option<(ShiftDirection, u128, &'tcx Expr<'tcx>)> { +fn parse_shift<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(ShiftDirection, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { if let ExprKind::Binary(op, l, r) = expr.kind { let dir = match op.node { BinOpKind::Shl => ShiftDirection::Left, BinOpKind::Shr => ShiftDirection::Right, _ => return None, }; - let const_expr = ConstEvalCtxt::new(cx).eval_local(r, expr.span.ctxt())?; - if let Constant::Int(shift) = const_expr { - return Some((dir, shift, l)); - } + return Some((dir, l, r)); } None } @@ -78,8 +72,8 @@ impl LateLintPass<'_> for ManualRotate { fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let ExprKind::Binary(op, l, r) = expr.kind && let BinOpKind::Add | BinOpKind::BitOr = op.node - && let Some((l_shift_dir, l_amount, l_expr)) = parse_shift(cx, l) - && let Some((r_shift_dir, r_amount, r_expr)) = parse_shift(cx, r) + && let Some((l_shift_dir, l_expr, l_amount)) = parse_shift(l) + && let Some((r_shift_dir, r_expr, r_amount)) = parse_shift(r) && l_shift_dir != r_shift_dir && clippy_utils::eq_expr_value(cx, l_expr, r_expr) && let Some(bit_width) = match cx.typeck_results().expr_ty(expr).kind() { @@ -87,24 +81,31 @@ impl LateLintPass<'_> for ManualRotate { ty::Uint(itype) => itype.bit_width(), _ => return, } - && l_amount + r_amount == u128::from(bit_width) { - let (shift_function, amount) = if l_amount < r_amount { - (l_shift_dir, l_amount) - } else { - (r_shift_dir, r_amount) - }; - let mut applicability = Applicability::MachineApplicable; - let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); - span_lint_and_sugg( - cx, - MANUAL_ROTATE, - expr.span, - "there is no need to manually implement bit rotation", - "this expression can be rewritten as", - format!("{expr_sugg}.{shift_function}({amount})"), - Applicability::MachineApplicable, - ); + let const_eval = ConstEvalCtxt::new(cx); + + let ctxt = expr.span.ctxt(); + if let Some(Constant::Int(l_amount)) = const_eval.eval_local(l_amount, ctxt) + && let Some(Constant::Int(r_amount)) = const_eval.eval_local(r_amount, ctxt) + && l_amount + r_amount == u128::from(bit_width) + { + let (shift_function, amount) = if l_amount < r_amount { + (l_shift_dir, l_amount) + } else { + (r_shift_dir, r_amount) + }; + let mut applicability = Applicability::MachineApplicable; + let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); + span_lint_and_sugg( + cx, + MANUAL_ROTATE, + expr.span, + "there is no need to manually implement bit rotation", + "this expression can be rewritten as", + format!("{expr_sugg}.{shift_function}({amount})"), + Applicability::MachineApplicable, + ); + } } } } From 4423e05f2558e0c00ada7a4a46ed9cfaebd457ad Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 3 Aug 2025 20:52:53 +0200 Subject: [PATCH 12/89] fix: print actual consts when shifting by them this is kind of a side-effect of the next commit --- clippy_lints/src/manual_rotate.rs | 9 +++++---- tests/ui/manual_rotate.fixed | 4 ++++ tests/ui/manual_rotate.rs | 4 ++++ tests/ui/manual_rotate.stderr | 30 ++++++++++++++++++------------ 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/manual_rotate.rs b/clippy_lints/src/manual_rotate.rs index d531746830f64..63bdf67a840f9 100644 --- a/clippy_lints/src/manual_rotate.rs +++ b/clippy_lints/src/manual_rotate.rs @@ -85,17 +85,18 @@ impl LateLintPass<'_> for ManualRotate { let const_eval = ConstEvalCtxt::new(cx); let ctxt = expr.span.ctxt(); - if let Some(Constant::Int(l_amount)) = const_eval.eval_local(l_amount, ctxt) - && let Some(Constant::Int(r_amount)) = const_eval.eval_local(r_amount, ctxt) - && l_amount + r_amount == u128::from(bit_width) + if let Some(Constant::Int(l_amount_val)) = const_eval.eval_local(l_amount, ctxt) + && let Some(Constant::Int(r_amount_val)) = const_eval.eval_local(r_amount, ctxt) + && l_amount_val + r_amount_val == u128::from(bit_width) { - let (shift_function, amount) = if l_amount < r_amount { + let (shift_function, amount) = if l_amount_val < r_amount_val { (l_shift_dir, l_amount) } else { (r_shift_dir, r_amount) }; let mut applicability = Applicability::MachineApplicable; let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); + let amount = sugg::Sugg::hir_with_applicability(cx, amount, "_", &mut applicability); span_lint_and_sugg( cx, MANUAL_ROTATE, diff --git a/tests/ui/manual_rotate.fixed b/tests/ui/manual_rotate.fixed index 49db8661369e9..c11804a32c78c 100644 --- a/tests/ui/manual_rotate.fixed +++ b/tests/ui/manual_rotate.fixed @@ -4,6 +4,7 @@ fn main() { let (x_u8, x_u16, x_u32, x_u64) = (1u8, 1u16, 1u32, 1u64); let (x_i8, x_i16, x_i32, x_i64) = (1i8, 1i16, 1i32, 1i64); let a_u32 = 1u32; + const N: u32 = 5; // True positives let y_u8 = x_u8.rotate_right(3); //~^ manual_rotate @@ -29,6 +30,9 @@ fn main() { //~^ manual_rotate let y_u64_as = (x_u32 as u64).rotate_right(8); //~^ manual_rotate + // shift by a const + let _ = x_i64.rotate_right(N); + //~^ manual_rotate // False positives - can't be replaced with a rotation let y_u8_false = (x_u8 >> 6) | (x_u8 << 3); diff --git a/tests/ui/manual_rotate.rs b/tests/ui/manual_rotate.rs index 6445e60aa25da..577d9aa20d879 100644 --- a/tests/ui/manual_rotate.rs +++ b/tests/ui/manual_rotate.rs @@ -4,6 +4,7 @@ fn main() { let (x_u8, x_u16, x_u32, x_u64) = (1u8, 1u16, 1u32, 1u64); let (x_i8, x_i16, x_i32, x_i64) = (1i8, 1i16, 1i32, 1i64); let a_u32 = 1u32; + const N: u32 = 5; // True positives let y_u8 = (x_u8 >> 3) | (x_u8 << 5); //~^ manual_rotate @@ -29,6 +30,9 @@ fn main() { //~^ manual_rotate let y_u64_as = (x_u32 as u64 >> 8) | ((x_u32 as u64) << 56); //~^ manual_rotate + // shift by a const + let _ = (x_i64 >> N) | (x_i64 << (64 - N)); + //~^ manual_rotate // False positives - can't be replaced with a rotation let y_u8_false = (x_u8 >> 6) | (x_u8 << 3); diff --git a/tests/ui/manual_rotate.stderr b/tests/ui/manual_rotate.stderr index a28721fbb94c8..8bae69931cbfe 100644 --- a/tests/ui/manual_rotate.stderr +++ b/tests/ui/manual_rotate.stderr @@ -1,5 +1,5 @@ error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:8:16 + --> tests/ui/manual_rotate.rs:9:16 | LL | let y_u8 = (x_u8 >> 3) | (x_u8 << 5); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u8.rotate_right(3)` @@ -8,64 +8,70 @@ LL | let y_u8 = (x_u8 >> 3) | (x_u8 << 5); = help: to override `-D warnings` add `#[allow(clippy::manual_rotate)]` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:10:17 + --> tests/ui/manual_rotate.rs:11:17 | LL | let y_u16 = (x_u16 >> 7) | (x_u16 << 9); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u16.rotate_right(7)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:12:17 + --> tests/ui/manual_rotate.rs:13:17 | LL | let y_u32 = (x_u32 >> 8) | (x_u32 << 24); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u32.rotate_right(8)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:14:17 + --> tests/ui/manual_rotate.rs:15:17 | LL | let y_u64 = (x_u64 >> 9) | (x_u64 << 55); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u64.rotate_right(9)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:16:16 + --> tests/ui/manual_rotate.rs:17:16 | LL | let y_i8 = (x_i8 >> 3) | (x_i8 << 5); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i8.rotate_right(3)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:18:17 + --> tests/ui/manual_rotate.rs:19:17 | LL | let y_i16 = (x_i16 >> 7) | (x_i16 << 9); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i16.rotate_right(7)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:20:17 + --> tests/ui/manual_rotate.rs:21:17 | LL | let y_i32 = (x_i32 >> 8) | (x_i32 << 24); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i32.rotate_right(8)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:22:17 + --> tests/ui/manual_rotate.rs:23:17 | LL | let y_i64 = (x_i64 >> 9) | (x_i64 << 55); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i64.rotate_right(9)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:25:22 + --> tests/ui/manual_rotate.rs:26:22 | LL | let y_u32_plus = (x_u32 >> 8) + (x_u32 << 24); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u32.rotate_right(8)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:28:25 + --> tests/ui/manual_rotate.rs:29:25 | LL | let y_u32_complex = ((x_u32 | 3256) >> 8) | ((x_u32 | 3256) << 24); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `(x_u32 | 3256).rotate_right(8)` error: there is no need to manually implement bit rotation - --> tests/ui/manual_rotate.rs:30:20 + --> tests/ui/manual_rotate.rs:31:20 | LL | let y_u64_as = (x_u32 as u64 >> 8) | ((x_u32 as u64) << 56); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `(x_u32 as u64).rotate_right(8)` -error: aborting due to 11 previous errors +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:34:13 + | +LL | let _ = (x_i64 >> N) | (x_i64 << (64 - N)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i64.rotate_right(N)` + +error: aborting due to 12 previous errors From b249f134239f576ece015a9406f7ebb579c02f2f Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 3 Aug 2025 20:45:21 +0200 Subject: [PATCH 13/89] feat: also detect non-consts --- clippy_lints/src/manual_rotate.rs | 51 ++++++++++++++++++++++--------- tests/ui/manual_rotate.fixed | 17 +++++++++++ tests/ui/manual_rotate.rs | 17 +++++++++++ tests/ui/manual_rotate.stderr | 20 +++++++++++- 4 files changed, 89 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/manual_rotate.rs b/clippy_lints/src/manual_rotate.rs index 63bdf67a840f9..e8db44698d9ce 100644 --- a/clippy_lints/src/manual_rotate.rs +++ b/clippy_lints/src/manual_rotate.rs @@ -85,28 +85,49 @@ impl LateLintPass<'_> for ManualRotate { let const_eval = ConstEvalCtxt::new(cx); let ctxt = expr.span.ctxt(); - if let Some(Constant::Int(l_amount_val)) = const_eval.eval_local(l_amount, ctxt) + let (shift_function, amount) = if let Some(Constant::Int(l_amount_val)) = + const_eval.eval_local(l_amount, ctxt) && let Some(Constant::Int(r_amount_val)) = const_eval.eval_local(r_amount, ctxt) && l_amount_val + r_amount_val == u128::from(bit_width) { - let (shift_function, amount) = if l_amount_val < r_amount_val { + if l_amount_val < r_amount_val { (l_shift_dir, l_amount) } else { (r_shift_dir, r_amount) + } + } else { + let (amount1, binop, minuend, amount2, shift_direction) = match (l_amount.kind, r_amount.kind) { + (_, ExprKind::Binary(binop, minuend, other)) => (l_amount, binop, minuend, other, l_shift_dir), + (ExprKind::Binary(binop, minuend, other), _) => (r_amount, binop, minuend, other, r_shift_dir), + _ => return, }; - let mut applicability = Applicability::MachineApplicable; - let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); - let amount = sugg::Sugg::hir_with_applicability(cx, amount, "_", &mut applicability); - span_lint_and_sugg( - cx, - MANUAL_ROTATE, - expr.span, - "there is no need to manually implement bit rotation", - "this expression can be rewritten as", - format!("{expr_sugg}.{shift_function}({amount})"), - Applicability::MachineApplicable, - ); - } + + if let Some(Constant::Int(minuend)) = const_eval.eval_local(minuend, ctxt) + && clippy_utils::eq_expr_value(cx, amount1, amount2) + // (x << s) | (x >> bit_width - s) + && ((binop.node == BinOpKind::Sub && u128::from(bit_width) == minuend) + // (x << s) | (x >> (bit_width - 1) ^ s) + || (binop.node == BinOpKind::BitXor && u128::from(bit_width).checked_sub(minuend) == Some(1))) + { + // NOTE: we take these from the side that _doesn't_ have the binop, since it's probably simpler + (shift_direction, amount1) + } else { + return; + } + }; + + let mut applicability = Applicability::MachineApplicable; + let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren(); + let amount = sugg::Sugg::hir_with_applicability(cx, amount, "_", &mut applicability); + span_lint_and_sugg( + cx, + MANUAL_ROTATE, + expr.span, + "there is no need to manually implement bit rotation", + "this expression can be rewritten as", + format!("{expr_sugg}.{shift_function}({amount})"), + Applicability::MachineApplicable, + ); } } } diff --git a/tests/ui/manual_rotate.fixed b/tests/ui/manual_rotate.fixed index c11804a32c78c..1012ffc1aa2d8 100644 --- a/tests/ui/manual_rotate.fixed +++ b/tests/ui/manual_rotate.fixed @@ -44,3 +44,20 @@ fn main() { let mut l = vec![12_u8, 34]; let y = (l.pop().unwrap() << 3) + (l.pop().unwrap() >> 5); } + +fn issue13028() { + let s = 5; + let u = 5; + let x: u32 = 123456; + + let _ = x.rotate_left(s); + //~^ manual_rotate + let _ = x.rotate_left(s); + //~^ manual_rotate + // still works with consts + let _ = x.rotate_right(9); + //~^ manual_rotate + + // don't lint, because `s` and `u` are different variables, albeit with the same value + let _ = (x << s) | (x >> (32 - u)); +} diff --git a/tests/ui/manual_rotate.rs b/tests/ui/manual_rotate.rs index 577d9aa20d879..3cdc79673c816 100644 --- a/tests/ui/manual_rotate.rs +++ b/tests/ui/manual_rotate.rs @@ -44,3 +44,20 @@ fn main() { let mut l = vec![12_u8, 34]; let y = (l.pop().unwrap() << 3) + (l.pop().unwrap() >> 5); } + +fn issue13028() { + let s = 5; + let u = 5; + let x: u32 = 123456; + + let _ = (x << s) | (x >> (32 - s)); + //~^ manual_rotate + let _ = (x << s) | (x >> (31 ^ s)); + //~^ manual_rotate + // still works with consts + let _ = (x >> 9) | (x << (32 - 9)); + //~^ manual_rotate + + // don't lint, because `s` and `u` are different variables, albeit with the same value + let _ = (x << s) | (x >> (32 - u)); +} diff --git a/tests/ui/manual_rotate.stderr b/tests/ui/manual_rotate.stderr index 8bae69931cbfe..ea04ee028db67 100644 --- a/tests/ui/manual_rotate.stderr +++ b/tests/ui/manual_rotate.stderr @@ -73,5 +73,23 @@ error: there is no need to manually implement bit rotation LL | let _ = (x_i64 >> N) | (x_i64 << (64 - N)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i64.rotate_right(N)` -error: aborting due to 12 previous errors +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:53:13 + | +LL | let _ = (x << s) | (x >> (32 - s)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x.rotate_left(s)` + +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:55:13 + | +LL | let _ = (x << s) | (x >> (31 ^ s)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x.rotate_left(s)` + +error: there is no need to manually implement bit rotation + --> tests/ui/manual_rotate.rs:58:13 + | +LL | let _ = (x >> 9) | (x << (32 - 9)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x.rotate_right(9)` + +error: aborting due to 15 previous errors From 0ba022a858c200bc2e7ea481995ee194980c5711 Mon Sep 17 00:00:00 2001 From: Zihan Date: Sat, 4 Oct 2025 10:51:21 -0400 Subject: [PATCH 14/89] `legacy_numeric_constants`: add ctxt check for internal macro changelog: none Signed-off-by: Zihan --- clippy_lints/src/legacy_numeric_constants.rs | 30 +++++-------------- .../ui/legacy_numeric_constants_unfixable.rs | 11 +++++++ 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/clippy_lints/src/legacy_numeric_constants.rs b/clippy_lints/src/legacy_numeric_constants.rs index 42c636505c015..8a5d97294d2bd 100644 --- a/clippy_lints/src/legacy_numeric_constants.rs +++ b/clippy_lints/src/legacy_numeric_constants.rs @@ -113,35 +113,18 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { // since this would only require removing a `use` import (which is already linted). && !is_numeric_const_path_canonical(path, [*mod_name, *name]) { - ( - vec![(expr.span, format!("{mod_name}::{name}"))], - "usage of a legacy numeric constant", - ) + (format!("{mod_name}::{name}"), "usage of a legacy numeric constant") // `::xxx_value` check } else if let ExprKind::Call(func, []) = &expr.kind && let ExprKind::Path(qpath) = &func.kind && let QPath::TypeRelative(ty, last_segment) = qpath && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() && is_integer_method(cx, def_id) + && let Some(mod_name) = ty.span.get_source_text(cx) + && ty.span.eq_ctxt(last_segment.ident.span) { - let mut sugg = vec![ - // Replace the function name up to the end by the constant name - ( - last_segment.ident.span.to(expr.span.shrink_to_hi()), - last_segment.ident.name.as_str()[..=2].to_ascii_uppercase(), - ), - ]; - let before_span = expr.span.shrink_to_lo().until(ty.span); - if !before_span.is_empty() { - // Remove everything before the type name - sugg.push((before_span, String::new())); - } - // Use `::` between the type name and the constant - let between_span = ty.span.shrink_to_hi().until(last_segment.ident.span); - if !between_span.check_source_text(cx, |s| s == "::") { - sugg.push((between_span, String::from("::"))); - } - (sugg, "usage of a legacy numeric method") + let name = last_segment.ident.name.as_str()[..=2].to_ascii_uppercase(); + (format!("{mod_name}::{name}"), "usage of a legacy numeric method") } else { return; }; @@ -151,7 +134,8 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { && !is_from_proc_macro(cx, expr) { span_lint_and_then(cx, LEGACY_NUMERIC_CONSTANTS, expr.span, msg, |diag| { - diag.multipart_suggestion_verbose( + diag.span_suggestion_verbose( + expr.span, "use the associated constant instead", sugg, Applicability::MaybeIncorrect, diff --git a/tests/ui/legacy_numeric_constants_unfixable.rs b/tests/ui/legacy_numeric_constants_unfixable.rs index 9bf0f7f355aee..084d97fdc0f74 100644 --- a/tests/ui/legacy_numeric_constants_unfixable.rs +++ b/tests/ui/legacy_numeric_constants_unfixable.rs @@ -77,3 +77,14 @@ fn msrv_juust_right() { use std::u32::MAX; //~^ ERROR: importing a legacy numeric constant } + +macro_rules! foo { + ($a: ty) => { + let _ = <$a>::max_value(); + let _ = (<$a>::max_value)(); + }; +} + +fn issue15805() { + foo!(u8); +} From 843f4d848809e1e3593b2c743232bd0bfe7d5db8 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 6 Oct 2025 17:49:36 +0200 Subject: [PATCH 15/89] Merge commit '2dc84cb744ad19d187871afb0385a616d80c209d' into clippy-subtree-update --- .github/workflows/clippy_mq.yml | 2 +- .github/workflows/lintcheck.yml | 2 +- .github/workflows/lintcheck_summary.yml | 4 +- CHANGELOG.md | 4 +- Cargo.toml | 7 +- book/src/development/trait_checking.md | 11 +- book/src/lint_configuration.md | 5 +- clippy_config/src/conf.rs | 5 +- clippy_lints/Cargo.toml | 7 +- clippy_lints/src/assertions_on_constants.rs | 103 ++-- .../src/cargo/lint_groups_priority.rs | 186 +++---- clippy_lints/src/cognitive_complexity.rs | 4 +- clippy_lints/src/collapsible_if.rs | 78 ++- clippy_lints/src/declared_lints.rs | 15 +- .../src/default_constructed_unit_structs.rs | 2 +- clippy_lints/src/deprecated_lints.rs | 8 +- clippy_lints/src/dereference.rs | 10 +- .../src/derive/derive_ord_xor_partial_ord.rs | 7 +- .../derive/derive_partial_eq_without_eq.rs | 13 +- .../src/derive/derived_hash_with_manual_eq.rs | 10 +- .../src/derive/expl_impl_clone_on_copy.rs | 20 +- clippy_lints/src/derive/mod.rs | 16 +- .../src/derive/unsafe_derive_deserialize.rs | 12 +- clippy_lints/src/doc/mod.rs | 2 +- clippy_lints/src/double_parens.rs | 73 ++- clippy_lints/src/enum_clike.rs | 8 +- clippy_lints/src/floating_point_arithmetic.rs | 21 +- clippy_lints/src/if_not_else.rs | 12 +- clippy_lints/src/if_then_some_else_none.rs | 1 + .../branches_sharing_code.rs} | 283 +--------- clippy_lints/src/ifs/if_same_then_else.rs | 29 + clippy_lints/src/ifs/ifs_same_cond.rs | 46 ++ clippy_lints/src/ifs/mod.rs | 182 ++++++ .../src/ifs/same_functions_in_if_cond.rs | 31 ++ clippy_lints/src/implicit_return.rs | 4 +- clippy_lints/src/implicit_saturating_add.rs | 5 +- clippy_lints/src/instant_subtraction.rs | 151 ----- .../src/invalid_upcast_comparisons.rs | 2 +- clippy_lints/src/item_name_repetitions.rs | 180 +++--- clippy_lints/src/lib.rs | 20 +- clippy_lints/src/macro_metavars_in_unsafe.rs | 9 +- clippy_lints/src/manual_float_methods.rs | 29 +- clippy_lints/src/manual_rem_euclid.rs | 20 +- clippy_lints/src/manual_rotate.rs | 2 +- clippy_lints/src/manual_strip.rs | 24 +- clippy_lints/src/matches/manual_unwrap_or.rs | 2 +- clippy_lints/src/matches/match_same_arms.rs | 54 +- clippy_lints/src/matches/overlapping_arms.rs | 6 +- .../src/matches/redundant_pattern_match.rs | 147 +++-- clippy_lints/src/matches/single_match.rs | 27 +- clippy_lints/src/mem_replace.rs | 13 +- clippy_lints/src/methods/filter_next.rs | 91 +-- .../src/methods/inefficient_to_string.rs | 4 + clippy_lints/src/methods/ip_constant.rs | 4 +- .../src/methods/is_digit_ascii_radix.rs | 2 +- clippy_lints/src/methods/iter_nth_zero.rs | 2 +- clippy_lints/src/methods/iter_skip_zero.rs | 16 +- clippy_lints/src/methods/lib.rs | 70 +++ clippy_lints/src/methods/map_identity.rs | 80 ++- clippy_lints/src/methods/mod.rs | 348 ++---------- clippy_lints/src/methods/new_ret_no_self.rs | 46 ++ clippy_lints/src/methods/or_fun_call.rs | 370 ++++++------ .../src/methods/range_zip_with_len.rs | 96 +++- clippy_lints/src/methods/repeat_once.rs | 2 +- .../src/methods/should_implement_trait.rs | 156 ++++++ .../src/methods/single_char_add_str.rs | 80 ++- .../src/methods/single_char_insert_string.rs | 67 --- .../src/methods/single_char_push_string.rs | 65 --- clippy_lints/src/methods/str_splitn.rs | 2 +- .../src/methods/unnecessary_min_or_max.rs | 7 +- .../src/methods/wrong_self_convention.rs | 38 +- clippy_lints/src/minmax.rs | 20 +- clippy_lints/src/module_style.rs | 155 +++--- clippy_lints/src/mut_mut.rs | 135 ++++- clippy_lints/src/needless_pass_by_value.rs | 63 ++- clippy_lints/src/new_without_default.rs | 190 ++++--- clippy_lints/src/non_canonical_impls.rs | 291 +++++----- clippy_lints/src/nonstandard_macro_braces.rs | 91 ++- clippy_lints/src/only_used_in_recursion.rs | 153 +++-- .../src/operators/arithmetic_side_effects.rs | 4 +- .../src/operators/const_comparisons.rs | 2 +- clippy_lints/src/operators/duration_subsec.rs | 2 +- clippy_lints/src/operators/erasing_op.rs | 4 +- clippy_lints/src/operators/float_cmp.rs | 7 +- clippy_lints/src/operators/identity_op.rs | 32 +- .../src/operators/manual_is_multiple_of.rs | 9 +- clippy_lints/src/operators/mod.rs | 2 +- .../src/operators/modulo_arithmetic.rs | 6 +- .../src/operators/numeric_arithmetic.rs | 2 +- clippy_lints/src/question_mark.rs | 10 +- clippy_lints/src/ranges.rs | 6 +- clippy_lints/src/time_subtraction.rs | 216 +++++++ clippy_lints/src/unit_types/let_unit_value.rs | 81 ++- ...reference.rs => unnecessary_mut_passed.rs} | 33 +- clippy_lints/src/unnecessary_semicolon.rs | 3 +- clippy_lints/src/unwrap.rs | 50 +- clippy_lints/src/zero_div_zero.rs | 5 +- clippy_lints/src/zero_repeat_side_effects.rs | 117 ++-- clippy_utils/README.md | 2 +- clippy_utils/src/consts.rs | 526 ++++++++++++------ clippy_utils/src/higher.rs | 2 +- clippy_utils/src/hir_utils.rs | 8 +- clippy_utils/src/lib.rs | 28 +- clippy_utils/src/msrvs.rs | 9 +- clippy_utils/src/paths.rs | 18 +- clippy_utils/src/ptr.rs | 52 -- clippy_utils/src/sym.rs | 7 + clippy_utils/src/ty/mod.rs | 6 +- lintcheck/Cargo.toml | 2 +- rust-toolchain.toml | 2 +- tests/compile-test.rs | 22 +- .../duplicated_mod_names_14697/Cargo.stderr | 11 + .../duplicated_mod_names_14697/Cargo.toml | 11 + .../duplicated_mod_names_14697/src/foo.rs | 1 + .../duplicated_mod_names_14697/src/foo/bar.rs | 1 + .../duplicated_mod_names_14697/src/lib.rs | 4 + .../src/other/foo/mod.rs | 1 + .../src/other/mod.rs | 1 + .../module_style/fail_mod/Cargo.stderr | 16 +- .../Cargo.toml | 10 + .../foo/bar/Cargo.toml | 5 + .../foo/bar/src/foo.rs | 1 + .../foo/bar/src/lib.rs | 1 + .../src/lib.rs | 1 + .../with_path_attr_mod/Cargo.toml | 10 + .../with_path_attr_mod/src/bar/mod.rs | 1 + .../with_path_attr_mod/src/lib.rs | 4 + .../with_path_attr_no_mod/Cargo.toml | 10 + .../with_path_attr_no_mod/src/bar.rs | 1 + .../with_path_attr_no_mod/src/foo.rs | 2 + .../with_path_attr_no_mod/src/lib.rs | 3 + .../collapsible_if/collapsible_else_if.fixed | 19 +- .../collapsible_if/collapsible_else_if.rs | 19 +- .../conf_deprecated_key.rs | 4 +- .../conf_deprecated_key.stderr | 2 +- .../index_refutable_slice.fixed | 1 - .../index_refutable_slice.rs | 1 - .../index_refutable_slice.stderr | 4 +- .../conf_nonstandard_macro_braces.fixed | 8 + .../conf_nonstandard_macro_braces.rs | 8 + .../conf_nonstandard_macro_braces.stderr | 8 +- tests/ui/assertions_on_constants.rs | 30 +- tests/ui/assertions_on_constants.stderr | 92 ++- tests/ui/auxiliary/option_helpers.rs | 4 + tests/ui/bind_instead_of_map.fixed | 1 - tests/ui/bind_instead_of_map.rs | 1 - tests/ui/bind_instead_of_map.stderr | 6 +- .../shared_at_top_and_bottom.rs | 5 +- .../shared_at_top_and_bottom.stderr | 24 +- tests/ui/cast_abs_to_unsigned.fixed | 4 +- tests/ui/cast_abs_to_unsigned.rs | 4 +- tests/ui/checked_unwrap/if_let_chains.rs | 24 + tests/ui/checked_unwrap/if_let_chains.stderr | 29 + tests/ui/clone_on_copy.fixed | 65 ++- tests/ui/clone_on_copy.rs | 65 ++- tests/ui/clone_on_copy.stderr | 26 +- tests/ui/clone_on_ref_ptr.fixed | 51 ++ tests/ui/clone_on_ref_ptr.rs | 51 ++ tests/ui/clone_on_ref_ptr.stderr | 41 ++ tests/ui/collapsible_else_if.fixed | 27 + tests/ui/collapsible_else_if.rs | 27 + tests/ui/collapsible_else_if.stderr | 2 +- tests/ui/collapsible_else_if_unfixable.rs | 22 + tests/ui/collapsible_else_if_unfixable.stderr | 17 + tests/ui/collapsible_if.fixed | 18 + tests/ui/collapsible_if.rs | 18 + tests/ui/collapsible_if.stderr | 2 +- tests/ui/collapsible_if_unfixable.rs | 20 + tests/ui/collapsible_if_unfixable.stderr | 17 + tests/ui/const_comparisons.rs | 14 +- tests/ui/const_comparisons.stderr | 62 +-- tests/ui/const_is_empty.rs | 50 +- tests/ui/const_is_empty.stderr | 108 +--- tests/ui/crashes/ice-4775.rs | 2 +- tests/ui/default_trait_access.fixed | 2 +- tests/ui/default_trait_access.rs | 2 +- tests/ui/default_trait_access.stderr | 16 +- tests/ui/derive.rs | 13 + tests/ui/derive.stderr | 10 +- tests/ui/derive_ord_xor_partial_ord.rs | 15 + tests/ui/derived_hash_with_manual_eq.rs | 16 + tests/ui/double_parens.fixed | 99 ++++ tests/ui/double_parens.rs | 46 +- tests/ui/double_parens.stderr | 65 ++- tests/ui/duration_subsec.fixed | 3 +- tests/ui/duration_subsec.rs | 1 - tests/ui/duration_subsec.stderr | 8 +- tests/ui/explicit_counter_loop.rs | 24 +- tests/ui/explicit_deref_methods.fixed | 15 + tests/ui/explicit_deref_methods.rs | 15 + tests/ui/explicit_deref_methods.stderr | 26 +- tests/ui/explicit_write.fixed | 5 +- tests/ui/explicit_write.rs | 5 +- tests/ui/explicit_write.stderr | 34 +- tests/ui/fallible_impl_from.rs | 3 +- tests/ui/fallible_impl_from.stderr | 20 +- tests/ui/floating_point_log.fixed | 6 +- tests/ui/floating_point_log.rs | 2 - tests/ui/floating_point_log.stderr | 60 +- tests/ui/if_then_some_else_none.fixed | 12 + tests/ui/if_then_some_else_none.rs | 12 + .../if_let_slice_binding.fixed | 14 +- .../if_let_slice_binding.rs | 14 +- tests/ui/inefficient_to_string.fixed | 8 + tests/ui/inefficient_to_string.rs | 8 + tests/ui/inefficient_to_string.stderr | 12 +- tests/ui/infinite_iter.rs | 4 +- tests/ui/infinite_iter.stderr | 4 +- tests/ui/ip_constant.fixed | 45 +- tests/ui/ip_constant.rs | 9 - tests/ui/ip_constant.stderr | 146 +---- tests/ui/issue_2356.fixed | 3 +- tests/ui/issue_2356.rs | 3 +- tests/ui/issue_2356.stderr | 2 +- tests/ui/issue_4266.rs | 3 +- tests/ui/issue_4266.stderr | 6 +- tests/ui/let_unit.fixed | 52 +- tests/ui/let_unit.rs | 51 +- tests/ui/let_unit.stderr | 98 +++- tests/ui/manual_float_methods.rs | 15 +- tests/ui/manual_float_methods.stderr | 40 +- tests/ui/manual_instant_elapsed.fixed | 2 +- tests/ui/manual_instant_elapsed.rs | 2 +- tests/ui/manual_strip.stderr | 3 - tests/ui/match_single_binding.fixed | 37 +- tests/ui/match_single_binding.rs | 37 +- tests/ui/match_single_binding.stderr | 130 ++--- tests/ui/match_single_binding2.fixed | 3 +- tests/ui/match_single_binding2.rs | 3 +- tests/ui/match_single_binding2.stderr | 12 +- tests/ui/mem_replace.fixed | 6 + tests/ui/mem_replace.rs | 6 + tests/ui/mem_replace.stderr | 8 +- tests/ui/methods.rs | 20 + tests/ui/methods.stderr | 13 +- tests/ui/methods_fixable.fixed | 14 + tests/ui/methods_fixable.rs | 14 + tests/ui/methods_fixable.stderr | 14 +- tests/ui/module_inception.rs | 9 + tests/ui/mut_mut.fixed | 92 +++ tests/ui/mut_mut.rs | 11 +- tests/ui/mut_mut.stderr | 58 +- tests/ui/mut_mut_unfixable.rs | 42 ++ tests/ui/mut_mut_unfixable.stderr | 41 ++ tests/ui/mut_reference.stderr | 77 --- tests/ui/new_without_default.fixed | 89 ++- tests/ui/new_without_default.rs | 61 +- tests/ui/new_without_default.stderr | 99 +++- tests/ui/only_used_in_recursion.rs | 3 +- tests/ui/only_used_in_recursion.stderr | 70 +-- tests/ui/or_fun_call.fixed | 16 +- tests/ui/or_fun_call.rs | 16 +- tests/ui/or_fun_call.stderr | 116 ++-- tests/ui/question_mark.fixed | 25 + tests/ui/question_mark.rs | 32 ++ tests/ui/question_mark.stderr | 22 +- tests/ui/range.fixed | 16 +- tests/ui/range.rs | 16 +- tests/ui/range.stderr | 29 +- tests/ui/range_unfixable.rs | 14 + tests/ui/range_unfixable.stderr | 12 + tests/ui/rename.fixed | 2 + tests/ui/rename.rs | 2 + tests/ui/rename.stderr | 154 ++--- tests/ui/repeat_once.fixed | 3 +- tests/ui/repeat_once.rs | 1 - tests/ui/repeat_once.stderr | 14 +- ...tderr => method_list_1.edition2015.stderr} | 30 +- .../method_list_1.edition2021.stderr | 184 ++++++ tests/ui/should_impl_trait/method_list_1.rs | 3 + .../method_list_2.edition2015.stderr | 172 ++++++ .../method_list_2.edition2021.stderr | 184 ++++++ tests/ui/should_impl_trait/method_list_2.rs | 5 +- tests/ui/single_match.fixed | 5 +- tests/ui/single_match.rs | 5 +- tests/ui/single_match.stderr | 20 +- tests/ui/unchecked_duration_subtraction.fixed | 20 - tests/ui/unchecked_duration_subtraction.rs | 20 - .../ui/unchecked_duration_subtraction.stderr | 29 - tests/ui/unchecked_time_subtraction.fixed | 37 ++ tests/ui/unchecked_time_subtraction.rs | 37 ++ tests/ui/unchecked_time_subtraction.stderr | 53 ++ .../unchecked_time_subtraction_unfixable.rs | 22 + ...nchecked_time_subtraction_unfixable.stderr | 29 + tests/ui/unnecessary_clone.rs | 111 ---- tests/ui/unnecessary_clone.stderr | 62 --- ...nce.fixed => unnecessary_mut_passed.fixed} | 20 + ...reference.rs => unnecessary_mut_passed.rs} | 20 + tests/ui/unnecessary_mut_passed.stderr | 220 ++++++++ ...micolon_feature_stmt_expr_attributes.fixed | 13 + ..._semicolon_feature_stmt_expr_attributes.rs | 13 + ...icolon_feature_stmt_expr_attributes.stderr | 11 + tests/ui/zero_repeat_side_effects.fixed | 28 +- tests/ui/zero_repeat_side_effects.rs | 28 +- tests/ui/zero_repeat_side_effects.stderr | 133 ++++- triagebot.toml | 1 - 296 files changed, 6949 insertions(+), 4035 deletions(-) rename clippy_lints/src/{copies.rs => ifs/branches_sharing_code.rs} (63%) create mode 100644 clippy_lints/src/ifs/if_same_then_else.rs create mode 100644 clippy_lints/src/ifs/ifs_same_cond.rs create mode 100644 clippy_lints/src/ifs/mod.rs create mode 100644 clippy_lints/src/ifs/same_functions_in_if_cond.rs delete mode 100644 clippy_lints/src/instant_subtraction.rs create mode 100644 clippy_lints/src/methods/lib.rs create mode 100644 clippy_lints/src/methods/new_ret_no_self.rs create mode 100644 clippy_lints/src/methods/should_implement_trait.rs delete mode 100644 clippy_lints/src/methods/single_char_insert_string.rs delete mode 100644 clippy_lints/src/methods/single_char_push_string.rs create mode 100644 clippy_lints/src/time_subtraction.rs rename clippy_lints/src/{mut_reference.rs => unnecessary_mut_passed.rs} (72%) delete mode 100644 clippy_utils/src/ptr.rs create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.stderr create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.toml create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo.rs create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo/bar.rs create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/src/lib.rs create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/foo/mod.rs create mode 100644 tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/mod.rs create mode 100644 tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/Cargo.toml create mode 100644 tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/Cargo.toml create mode 100644 tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/foo.rs create mode 100644 tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/lib.rs create mode 100644 tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/src/lib.rs create mode 100644 tests/ui-cargo/module_style/with_path_attr_mod/Cargo.toml create mode 100644 tests/ui-cargo/module_style/with_path_attr_mod/src/bar/mod.rs create mode 100644 tests/ui-cargo/module_style/with_path_attr_mod/src/lib.rs create mode 100644 tests/ui-cargo/module_style/with_path_attr_no_mod/Cargo.toml create mode 100644 tests/ui-cargo/module_style/with_path_attr_no_mod/src/bar.rs create mode 100644 tests/ui-cargo/module_style/with_path_attr_no_mod/src/foo.rs create mode 100644 tests/ui-cargo/module_style/with_path_attr_no_mod/src/lib.rs create mode 100644 tests/ui/checked_unwrap/if_let_chains.rs create mode 100644 tests/ui/checked_unwrap/if_let_chains.stderr create mode 100644 tests/ui/clone_on_ref_ptr.fixed create mode 100644 tests/ui/clone_on_ref_ptr.rs create mode 100644 tests/ui/clone_on_ref_ptr.stderr create mode 100644 tests/ui/collapsible_else_if_unfixable.rs create mode 100644 tests/ui/collapsible_else_if_unfixable.stderr create mode 100644 tests/ui/collapsible_if_unfixable.rs create mode 100644 tests/ui/collapsible_if_unfixable.stderr create mode 100644 tests/ui/double_parens.fixed create mode 100644 tests/ui/mut_mut.fixed create mode 100644 tests/ui/mut_mut_unfixable.rs create mode 100644 tests/ui/mut_mut_unfixable.stderr delete mode 100644 tests/ui/mut_reference.stderr create mode 100644 tests/ui/range_unfixable.rs create mode 100644 tests/ui/range_unfixable.stderr rename tests/ui/should_impl_trait/{method_list_1.stderr => method_list_1.edition2015.stderr} (86%) create mode 100644 tests/ui/should_impl_trait/method_list_1.edition2021.stderr create mode 100644 tests/ui/should_impl_trait/method_list_2.edition2015.stderr create mode 100644 tests/ui/should_impl_trait/method_list_2.edition2021.stderr delete mode 100644 tests/ui/unchecked_duration_subtraction.fixed delete mode 100644 tests/ui/unchecked_duration_subtraction.rs delete mode 100644 tests/ui/unchecked_duration_subtraction.stderr create mode 100644 tests/ui/unchecked_time_subtraction.fixed create mode 100644 tests/ui/unchecked_time_subtraction.rs create mode 100644 tests/ui/unchecked_time_subtraction.stderr create mode 100644 tests/ui/unchecked_time_subtraction_unfixable.rs create mode 100644 tests/ui/unchecked_time_subtraction_unfixable.stderr delete mode 100644 tests/ui/unnecessary_clone.rs delete mode 100644 tests/ui/unnecessary_clone.stderr rename tests/ui/{mut_reference.fixed => unnecessary_mut_passed.fixed} (87%) rename tests/ui/{mut_reference.rs => unnecessary_mut_passed.rs} (87%) create mode 100644 tests/ui/unnecessary_mut_passed.stderr create mode 100644 tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.fixed create mode 100644 tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.rs create mode 100644 tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.stderr diff --git a/.github/workflows/clippy_mq.yml b/.github/workflows/clippy_mq.yml index 0bcb713593593..9d099137449ef 100644 --- a/.github/workflows/clippy_mq.yml +++ b/.github/workflows/clippy_mq.yml @@ -181,7 +181,7 @@ jobs: # Download - name: Download target dir - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: binaries path: target/debug diff --git a/.github/workflows/lintcheck.yml b/.github/workflows/lintcheck.yml index 390d6a0f74758..45fd10ae76149 100644 --- a/.github/workflows/lintcheck.yml +++ b/.github/workflows/lintcheck.yml @@ -126,7 +126,7 @@ jobs: fail-on-cache-miss: true - name: Download JSON - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 - name: Store PR number run: echo ${{ github.event.pull_request.number }} > pr.txt diff --git a/.github/workflows/lintcheck_summary.yml b/.github/workflows/lintcheck_summary.yml index 52f52e155a07b..6768cd65701ac 100644 --- a/.github/workflows/lintcheck_summary.yml +++ b/.github/workflows/lintcheck_summary.yml @@ -27,7 +27,7 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Download artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: summary path: untrusted @@ -35,7 +35,7 @@ jobs: github-token: ${{ github.token }} - name: Format comment - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: | const fs = require("fs"); diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f26b9470e826..30781d3d33fba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ Current stable, released 2025-09-18 Note: This Clippy release does not introduce many new lints and is focused entirely on bug fixes — see [#15086](https://github.com/rust-lang/rust-clippy/issues/15086) for more details. -## New Lints +### New Lints * Added [`manual_is_multiple_of`] to `complexity` [#14292](https://github.com/rust-lang/rust-clippy/pull/14292) * Added [`doc_broken_link`] to `pedantic` [#13696](https://github.com/rust-lang/rust-clippy/pull/13696) @@ -6598,6 +6598,7 @@ Released 2018-09-13 [`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment [`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors [`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files +[`self_only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_only_used_in_recursion [`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned [`semicolon_inside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_inside_block [`semicolon_outside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_outside_block @@ -6703,6 +6704,7 @@ Released 2018-09-13 [`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds [`unbuffered_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#unbuffered_bytes [`unchecked_duration_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction +[`unchecked_time_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_time_subtraction [`unconditional_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#unconditional_recursion [`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks [`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops diff --git a/Cargo.toml b/Cargo.toml index e06383499893a..bedcc300f8567 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,13 +38,18 @@ ui_test = "0.30.2" regex = "1.5.5" serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.122" -toml = "0.7.3" walkdir = "2.3" filetime = "0.2.9" itertools = "0.12" pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] } askama = { version = "0.14", default-features = false, features = ["alloc", "config", "derive"] } +[dev-dependencies.toml] +version = "0.9.7" +default-features = false +# preserve_order keeps diagnostic output in file order +features = ["parse", "preserve_order"] + [build-dependencies] rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } diff --git a/book/src/development/trait_checking.md b/book/src/development/trait_checking.md index 6d01496eebe07..c6f6f6bd2f99e 100644 --- a/book/src/development/trait_checking.md +++ b/book/src/development/trait_checking.md @@ -24,12 +24,11 @@ use rustc_span::symbol::sym; impl LateLintPass<'_> for CheckIteratorTraitLint { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - let implements_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| { - implements_trait(cx, cx.typeck_results().expr_ty(expr), id, &[]) - }); - if implements_iterator { - // [...] - } + let implements_iterator = (cx.tcx.get_diagnostic_item(sym::Iterator)) + .is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(expr), id, &[])); + if implements_iterator { + // [...] + } } } diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index c2d080cd96a1d..b2ba19631f133 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -845,6 +845,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`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) +* [`inefficient_to_string`](https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string) * [`io_other_error`](https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error) * [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map) * [`legacy_numeric_constants`](https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants) @@ -883,6 +884,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) * [`non_std_lazy_statics`](https://rust-lang.github.io/rust-clippy/master/index.html#non_std_lazy_statics) * [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref) +* [`or_fun_call`](https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call) * [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) * [`question_mark`](https://rust-lang.github.io/rust-clippy/master/index.html#question_mark) * [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names) @@ -894,9 +896,10 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`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) +* [`unchecked_time_subtraction`](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_time_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) +* [`unnecessary_unwrap`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap) * [`unnested_or_patterns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns) * [`unused_trait_names`](https://rust-lang.github.io/rust-clippy/master/index.html#unused_trait_names) * [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 2f28f6175ad82..9ad434604dfcf 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -741,6 +741,7 @@ define_Conf! { from_over_into, if_then_some_else_none, index_refutable_slice, + inefficient_to_string, io_other_error, iter_kv_map, legacy_numeric_constants, @@ -779,6 +780,7 @@ define_Conf! { needless_borrow, non_std_lazy_statics, option_as_ref_deref, + or_fun_call, ptr_as_ptr, question_mark, redundant_field_names, @@ -790,9 +792,10 @@ define_Conf! { transmute_ptr_to_ref, tuple_array_conversions, type_repetition_in_bounds, - unchecked_duration_subtraction, + unchecked_time_subtraction, uninlined_format_args, unnecessary_lazy_evaluations, + unnecessary_unwrap, unnested_or_patterns, unused_trait_names, use_self, diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 51e59ae205076..42486e182ee34 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -18,12 +18,17 @@ itertools = "0.12" quine-mc_cluskey = "0.2" regex-syntax = "0.8" serde = { version = "1.0", features = ["derive"] } -toml = "0.7.3" unicode-normalization = "0.1" unicode-script = { version = "0.5", default-features = false } semver = "1.0" url = "2.2" +[dependencies.toml] +version = "0.9.7" +default-features = false +# preserve_order keeps diagnostic output in file order +features = ["parse", "preserve_order"] + [dev-dependencies] walkdir = "2.3" diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index b6684825835a9..2586c89bc8680 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -1,10 +1,13 @@ +use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_inside_always_const_context; -use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node}; +use clippy_utils::macros::{find_assert_args, root_macro_call_first_node}; +use clippy_utils::msrvs::Msrv; +use clippy_utils::{is_inside_always_const_context, msrvs}; +use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::sym; declare_clippy_lint! { @@ -28,56 +31,60 @@ declare_clippy_lint! { "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`" } -declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]); +impl_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]); +pub struct AssertionsOnConstants { + msrv: Msrv, +} +impl AssertionsOnConstants { + pub fn new(conf: &Conf) -> Self { + Self { msrv: conf.msrv } + } +} impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, e) else { - return; - }; - let is_debug = match cx.tcx.get_diagnostic_name(macro_call.def_id) { - Some(sym::debug_assert_macro) => true, - Some(sym::assert_macro) => false, - _ => return, - }; - let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { - return; - }; - let Some(Constant::Bool(val)) = ConstEvalCtxt::new(cx).eval(condition) else { - return; - }; + if let Some(macro_call) = root_macro_call_first_node(cx, e) + && let is_debug = match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::debug_assert_macro) => true, + Some(sym::assert_macro) => false, + _ => return, + } + && let Some((condition, _)) = find_assert_args(cx, e, macro_call.expn) + && let Some((Constant::Bool(assert_val), const_src)) = + ConstEvalCtxt::new(cx).eval_with_source(condition, macro_call.span.ctxt()) + && let in_const_context = is_inside_always_const_context(cx.tcx, e.hir_id) + && (const_src.is_local() || !in_const_context) + && !(is_debug && as_bool_lit(condition) == Some(false)) + { + let (msg, help) = if !const_src.is_local() { + let help = if self.msrv.meets(cx, msrvs::CONST_BLOCKS) { + "consider moving this into a const block: `const { assert!(..) }`" + } else if self.msrv.meets(cx, msrvs::CONST_PANIC) { + "consider moving this to an anonymous constant: `const _: () = { assert!(..); }`" + } else { + return; + }; + ("this assertion has a constant value", help) + } else if assert_val { + ("this assertion is always `true`", "remove the assertion") + } else { + ( + "this assertion is always `false`", + "replace this with `panic!()` or `unreachable!()`", + ) + }; - match condition.kind { - ExprKind::Path(..) | ExprKind::Lit(_) => {}, - _ if is_inside_always_const_context(cx.tcx, e.hir_id) => return, - _ => {}, + span_lint_and_help(cx, ASSERTIONS_ON_CONSTANTS, macro_call.span, msg, None, help); } + } +} - if val { - span_lint_and_help( - cx, - ASSERTIONS_ON_CONSTANTS, - macro_call.span, - format!( - "`{}!(true)` will be optimized out by the compiler", - cx.tcx.item_name(macro_call.def_id) - ), - None, - "remove it", - ); - } else if !is_debug { - let (assert_arg, panic_arg) = match panic_expn { - PanicExpn::Empty => ("", ""), - _ => (", ..", ".."), - }; - span_lint_and_help( - cx, - ASSERTIONS_ON_CONSTANTS, - macro_call.span, - format!("`assert!(false{assert_arg})` should probably be replaced"), - None, - format!("use `panic!({panic_arg})` or `unreachable!({panic_arg})`"), - ); - } +fn as_bool_lit(e: &Expr<'_>) -> Option { + if let ExprKind::Lit(l) = e.kind + && let LitKind::Bool(b) = l.node + { + Some(b) + } else { + None } } diff --git a/clippy_lints/src/cargo/lint_groups_priority.rs b/clippy_lints/src/cargo/lint_groups_priority.rs index ffd6c520c9ae8..14c5e22fb9cd5 100644 --- a/clippy_lints/src/cargo/lint_groups_priority.rs +++ b/clippy_lints/src/cargo/lint_groups_priority.rs @@ -4,142 +4,103 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_lint::{LateContext, unerased_lint_store}; use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext}; -use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; use std::ops::Range; use std::path::Path; use toml::Spanned; +use toml::de::{DeTable, DeValue}; -#[derive(Deserialize, Serialize, Debug)] -struct LintConfigTable { - level: String, - priority: Option, +fn toml_span(range: Range, file: &SourceFile) -> Span { + Span::new( + file.start_pos + BytePos::from_usize(range.start), + file.start_pos + BytePos::from_usize(range.end), + SyntaxContext::root(), + None, + ) } -#[derive(Deserialize, Debug)] -#[serde(untagged)] -enum LintConfig { - Level(String), - Table(LintConfigTable), +struct LintConfig<'a> { + sp: Range, + level: &'a str, + priority: Option, } - -impl LintConfig { - fn level(&self) -> &str { - match self { - LintConfig::Level(level) => level, - LintConfig::Table(table) => &table.level, - } - } - +impl<'a> LintConfig<'a> { fn priority(&self) -> i64 { - match self { - LintConfig::Level(_) => 0, - LintConfig::Table(table) => table.priority.unwrap_or(0), - } + self.priority.unwrap_or(0) } fn is_implicit(&self) -> bool { - if let LintConfig::Table(table) = self { - table.priority.is_none() - } else { - true - } + self.priority.is_none() } -} - -type LintTable = BTreeMap, Spanned>; - -#[derive(Deserialize, Debug, Default)] -struct Lints { - #[serde(default)] - rust: LintTable, - #[serde(default)] - clippy: LintTable, -} - -#[derive(Deserialize, Debug, Default)] -struct Workspace { - #[serde(default)] - lints: Lints, -} -#[derive(Deserialize, Debug)] -struct CargoToml { - #[serde(default)] - lints: Lints, - #[serde(default)] - workspace: Workspace, -} - -fn toml_span(range: Range, file: &SourceFile) -> Span { - Span::new( - file.start_pos + BytePos::from_usize(range.start), - file.start_pos + BytePos::from_usize(range.end), - SyntaxContext::root(), - None, - ) + fn parse(value: &'a Spanned>) -> Option { + let sp = value.span(); + let (level, priority) = match value.get_ref() { + DeValue::String(level) => (&**level, None), + DeValue::Table(tbl) => { + let level = tbl.get("level")?.get_ref().as_str()?; + let priority = if let Some(priority) = tbl.get("priority") { + let priority = priority.get_ref().as_integer()?; + Some(i64::from_str_radix(priority.as_str(), priority.radix()).ok()?) + } else { + None + }; + (level, priority) + }, + _ => return None, + }; + Some(Self { sp, level, priority }) + } } -fn check_table(cx: &LateContext<'_>, table: LintTable, known_groups: &FxHashSet<&str>, file: &SourceFile) { +fn check_table(cx: &LateContext<'_>, table: &DeTable<'_>, known_groups: &FxHashSet<&str>, file: &SourceFile) { let mut lints = Vec::new(); let mut groups = Vec::new(); for (name, config) in table { - if name.get_ref() == "warnings" { - continue; - } - - if known_groups.contains(name.get_ref().as_str()) { - groups.push((name, config)); - } else { - lints.push((name, config.into_inner())); + if name.get_ref() != "warnings" + && let Some(config) = LintConfig::parse(config) + { + if known_groups.contains(&**name.get_ref()) { + groups.push((name, config)); + } else { + lints.push((name, config)); + } } } for (group, group_config) in groups { - let priority = group_config.get_ref().priority(); - let level = group_config.get_ref().level(); - if let Some((conflict, _)) = lints - .iter() - .rfind(|(_, lint_config)| lint_config.priority() == priority && lint_config.level() != level) - { + if let Some((conflict, _)) = lints.iter().rfind(|(_, lint_config)| { + lint_config.priority() == group_config.priority() && lint_config.level != group_config.level + }) { span_lint_and_then( cx, LINT_GROUPS_PRIORITY, toml_span(group.span(), file), format!( - "lint group `{}` has the same priority ({priority}) as a lint", - group.as_ref() + "lint group `{}` has the same priority ({}) as a lint", + group.as_ref(), + group_config.priority(), ), |diag| { - let config_span = toml_span(group_config.span(), file); + let config_span = toml_span(group_config.sp.clone(), file); - if group_config.as_ref().is_implicit() { + if group_config.is_implicit() { diag.span_label(config_span, "has an implicit priority of 0"); } diag.span_label(toml_span(conflict.span(), file), "has the same priority as this lint"); diag.note("the order of the lints in the table is ignored by Cargo"); - let mut suggestion = String::new(); let low_priority = lints .iter() - .map(|(_, config)| config.priority().saturating_sub(1)) + .map(|(_, lint_config)| lint_config.priority().saturating_sub(1)) .min() .unwrap_or(-1); - Serialize::serialize( - &LintConfigTable { - level: level.into(), - priority: Some(low_priority), - }, - toml::ser::ValueSerializer::new(&mut suggestion), - ) - .unwrap(); diag.span_suggestion_verbose( config_span, format!( "to have lints override the group set `{}` to a lower priority", group.as_ref() ), - suggestion, + format!("{{ level = {:?}, priority = {low_priority} }}", group_config.level,), Applicability::MaybeIncorrect, ); }, @@ -148,10 +109,29 @@ fn check_table(cx: &LateContext<'_>, table: LintTable, known_groups: &FxHashSet< } } +struct LintTbls<'a> { + rust: Option<&'a DeTable<'a>>, + clippy: Option<&'a DeTable<'a>>, +} +fn get_lint_tbls<'a>(tbl: &'a DeTable<'a>) -> LintTbls<'a> { + if let Some(lints) = tbl.get("lints") + && let Some(lints) = lints.get_ref().as_table() + { + let rust = lints.get("rust").and_then(|x| x.get_ref().as_table()); + let clippy = lints.get("clippy").and_then(|x| x.get_ref().as_table()); + LintTbls { rust, clippy } + } else { + LintTbls { + rust: None, + clippy: None, + } + } +} + pub fn check(cx: &LateContext<'_>) { if let Ok(file) = cx.tcx.sess.source_map().load_file(Path::new("Cargo.toml")) && let Some(src) = file.src.as_deref() - && let Ok(cargo_toml) = toml::from_str::(src) + && let Ok(cargo_toml) = DeTable::parse(src) { let mut rustc_groups = FxHashSet::default(); let mut clippy_groups = FxHashSet::default(); @@ -167,9 +147,23 @@ pub fn check(cx: &LateContext<'_>) { } } - check_table(cx, cargo_toml.lints.rust, &rustc_groups, &file); - check_table(cx, cargo_toml.lints.clippy, &clippy_groups, &file); - check_table(cx, cargo_toml.workspace.lints.rust, &rustc_groups, &file); - check_table(cx, cargo_toml.workspace.lints.clippy, &clippy_groups, &file); + let lints = get_lint_tbls(cargo_toml.get_ref()); + if let Some(lints) = lints.rust { + check_table(cx, lints, &rustc_groups, &file); + } + if let Some(lints) = lints.clippy { + check_table(cx, lints, &clippy_groups, &file); + } + if let Some(tbl) = cargo_toml.get_ref().get("workspace") + && let Some(tbl) = tbl.get_ref().as_table() + { + let lints = get_lint_tbls(tbl); + if let Some(lints) = lints.rust { + check_table(cx, lints, &rustc_groups, &file); + } + if let Some(lints) = lints.clippy { + check_table(cx, lints, &clippy_groups, &file); + } + } } } diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 7646aa48b7726..c0f30e456d8db 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{LimitStack, get_async_fn_body, is_async_fn, sym}; +use clippy_utils::{LimitStack, get_async_fn_body, sym}; use core::ops::ControlFlow; use rustc_hir::intravisit::FnKind; use rustc_hir::{Attribute, Body, Expr, ExprKind, FnDecl}; @@ -147,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { def_id: LocalDefId, ) { if !cx.tcx.has_attr(def_id, sym::test) { - let expr = if is_async_fn(kind) { + let expr = if kind.asyncness().is_async() { match get_async_fn_body(cx.tcx, body) { Some(b) => b, None => { diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index ad610fbd8d2c3..b13e307a3f9c8 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -1,16 +1,16 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::msrvs::Msrv; use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block_with_applicability}; -use clippy_utils::{span_contains_non_whitespace, tokenize_with_text}; -use rustc_ast::BinOpKind; +use clippy_utils::{can_use_if_let_chains, span_contains_non_whitespace, sym, tokenize_with_text}; +use rustc_ast::{BinOpKind, MetaItemInner}; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, StmtKind}; use rustc_lexer::TokenKind; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, Level}; use rustc_session::impl_lint_pass; use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -95,14 +95,14 @@ impl CollapsibleIf { fn check_collapsible_else_if(&self, cx: &LateContext<'_>, then_span: Span, else_block: &Block<'_>) { if let Some(else_) = expr_block(else_block) - && cx.tcx.hir_attrs(else_.hir_id).is_empty() && !else_.span.from_expansion() && let ExprKind::If(else_if_cond, ..) = else_.kind - && !block_starts_with_significant_tokens(cx, else_block, else_, self.lint_commented_code) + && self.check_significant_tokens_and_expect_attrs(cx, else_block, else_, sym::collapsible_else_if) { - span_lint_and_then( + span_lint_hir_and_then( cx, COLLAPSIBLE_ELSE_IF, + else_.hir_id, else_block.span, "this `else { if .. }` block can be collapsed", |diag| { @@ -166,15 +166,15 @@ impl CollapsibleIf { fn check_collapsible_if_if(&self, cx: &LateContext<'_>, expr: &Expr<'_>, check: &Expr<'_>, then: &Block<'_>) { if let Some(inner) = expr_block(then) - && cx.tcx.hir_attrs(inner.hir_id).is_empty() && let ExprKind::If(check_inner, _, None) = &inner.kind && self.eligible_condition(cx, check_inner) && expr.span.eq_ctxt(inner.span) - && !block_starts_with_significant_tokens(cx, then, inner, self.lint_commented_code) + && self.check_significant_tokens_and_expect_attrs(cx, then, inner, sym::collapsible_if) { - span_lint_and_then( + span_lint_hir_and_then( cx, COLLAPSIBLE_IF, + inner.hir_id, expr.span, "this `if` statement can be collapsed", |diag| { @@ -216,8 +216,46 @@ impl CollapsibleIf { } fn eligible_condition(&self, cx: &LateContext<'_>, cond: &Expr<'_>) -> bool { - !matches!(cond.kind, ExprKind::Let(..)) - || (cx.tcx.sess.edition().at_least_rust_2024() && self.msrv.meets(cx, msrvs::LET_CHAINS)) + !matches!(cond.kind, ExprKind::Let(..)) || can_use_if_let_chains(cx, self.msrv) + } + + // Check that nothing significant can be found between the initial `{` of `inner_if` and + // the beginning of `inner_if_expr`... + // + // Unless it's only an `#[expect(clippy::collapsible{,_else}_if)]` attribute, in which case we + // _do_ need to lint, in order to actually fulfill its expectation (#13365) + fn check_significant_tokens_and_expect_attrs( + &self, + cx: &LateContext<'_>, + inner_if: &Block<'_>, + inner_if_expr: &Expr<'_>, + expected_lint_name: Symbol, + ) -> bool { + match cx.tcx.hir_attrs(inner_if_expr.hir_id) { + [] => { + // There aren't any attributes, so just check for significant tokens + let span = inner_if.span.split_at(1).1.until(inner_if_expr.span); + !span_contains_non_whitespace(cx, span, self.lint_commented_code) + }, + + [attr] + if matches!(Level::from_attr(attr), Some((Level::Expect, _))) + && let Some(metas) = attr.meta_item_list() + && let Some(MetaItemInner::MetaItem(meta_item)) = metas.first() + && let [tool, lint_name] = meta_item.path.segments.as_slice() + && tool.ident.name == sym::clippy + && [expected_lint_name, sym::style, sym::all].contains(&lint_name.ident.name) => + { + // There is an `expect` attribute -- check that there is no _other_ significant text + let span_before_attr = inner_if.span.split_at(1).1.until(attr.span()); + let span_after_attr = attr.span().between(inner_if_expr.span); + !span_contains_non_whitespace(cx, span_before_attr, self.lint_commented_code) + && !span_contains_non_whitespace(cx, span_after_attr, self.lint_commented_code) + }, + + // There are other attributes, which are significant tokens -- check failed + _ => false, + } } } @@ -242,18 +280,6 @@ impl LateLintPass<'_> for CollapsibleIf { } } -// Check that nothing significant can be found but whitespaces between the initial `{` of `block` -// and the beginning of `stop_at`. -fn block_starts_with_significant_tokens( - cx: &LateContext<'_>, - block: &Block<'_>, - stop_at: &Expr<'_>, - lint_commented_code: bool, -) -> bool { - let span = block.span.split_at(1).1.until(stop_at.span); - span_contains_non_whitespace(cx, span, lint_commented_code) -} - /// If `block` is a block with either one expression or a statement containing an expression, /// return the expression. We don't peel blocks recursively, as extra blocks might be intentional. fn expr_block<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 2a4bedc184552..0ec0aaaad4536 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -84,10 +84,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::collapsible_if::COLLAPSIBLE_IF_INFO, crate::collection_is_never_read::COLLECTION_IS_NEVER_READ_INFO, crate::comparison_chain::COMPARISON_CHAIN_INFO, - crate::copies::BRANCHES_SHARING_CODE_INFO, - crate::copies::IFS_SAME_COND_INFO, - crate::copies::IF_SAME_THEN_ELSE_INFO, - crate::copies::SAME_FUNCTIONS_IN_IF_CONDITION_INFO, crate::copy_iterator::COPY_ITERATOR_INFO, crate::crate_in_macro_def::CRATE_IN_MACRO_DEF_INFO, crate::create_dir::CREATE_DIR_INFO, @@ -204,6 +200,10 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::if_let_mutex::IF_LET_MUTEX_INFO, crate::if_not_else::IF_NOT_ELSE_INFO, crate::if_then_some_else_none::IF_THEN_SOME_ELSE_NONE_INFO, + crate::ifs::BRANCHES_SHARING_CODE_INFO, + crate::ifs::IFS_SAME_COND_INFO, + crate::ifs::IF_SAME_THEN_ELSE_INFO, + crate::ifs::SAME_FUNCTIONS_IN_IF_CONDITION_INFO, crate::ignored_unit_patterns::IGNORED_UNIT_PATTERNS_INFO, crate::impl_hash_with_borrow_str_and_bytes::IMPL_HASH_BORROW_WITH_STR_AND_BYTES_INFO, crate::implicit_hasher::IMPLICIT_HASHER_INFO, @@ -226,8 +226,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY_INFO, crate::init_numbered_fields::INIT_NUMBERED_FIELDS_INFO, crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO, - 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, @@ -535,7 +533,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::multiple_unsafe_ops_per_block::MULTIPLE_UNSAFE_OPS_PER_BLOCK_INFO, crate::mut_key::MUTABLE_KEY_TYPE_INFO, crate::mut_mut::MUT_MUT_INFO, - crate::mut_reference::UNNECESSARY_MUT_PASSED_INFO, crate::mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL_INFO, crate::mutex_atomic::MUTEX_ATOMIC_INFO, crate::mutex_atomic::MUTEX_INTEGER_INFO, @@ -576,6 +573,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO, crate::octal_escapes::OCTAL_ESCAPES_INFO, crate::only_used_in_recursion::ONLY_USED_IN_RECURSION_INFO, + crate::only_used_in_recursion::SELF_ONLY_USED_IN_RECURSION_INFO, crate::operators::ABSURD_EXTREME_COMPARISONS_INFO, crate::operators::ARITHMETIC_SIDE_EFFECTS_INFO, crate::operators::ASSIGN_OP_PATTERN_INFO, @@ -704,6 +702,8 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::tabs_in_doc_comments::TABS_IN_DOC_COMMENTS_INFO, crate::temporary_assignment::TEMPORARY_ASSIGNMENT_INFO, crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO, + crate::time_subtraction::MANUAL_INSTANT_ELAPSED_INFO, + crate::time_subtraction::UNCHECKED_TIME_SUBTRACTION_INFO, crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO, crate::to_string_trait_impl::TO_STRING_TRAIT_IMPL_INFO, crate::toplevel_ref_arg::TOPLEVEL_REF_ARG_INFO, @@ -751,6 +751,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::unnecessary_box_returns::UNNECESSARY_BOX_RETURNS_INFO, crate::unnecessary_literal_bound::UNNECESSARY_LITERAL_BOUND_INFO, crate::unnecessary_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO, + crate::unnecessary_mut_passed::UNNECESSARY_MUT_PASSED_INFO, crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO, crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO, crate::unnecessary_semicolon::UNNECESSARY_SEMICOLON_INFO, diff --git a/clippy_lints/src/default_constructed_unit_structs.rs b/clippy_lints/src/default_constructed_unit_structs.rs index f8a9037fc8047..641f8ae03b72a 100644 --- a/clippy_lints/src/default_constructed_unit_structs.rs +++ b/clippy_lints/src/default_constructed_unit_structs.rs @@ -75,7 +75,7 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs { && !base.is_suggestable_infer_ty() { let mut removals = vec![(expr.span.with_lo(qpath.qself_span().hi()), String::new())]; - if expr.span.with_source_text(cx, |s| s.starts_with('<')) == Some(true) { + if expr.span.check_source_text(cx, |s| s.starts_with('<')) { // Remove `<`, '>` has already been removed by the existing removal expression. removals.push((expr.span.with_hi(qpath.qself_span().lo()), String::new())); } diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 88aebc3e6a168..2147f72889093 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -18,11 +18,11 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [ ("clippy::assign_ops", "compound operators are harmless and linting on them is not in scope for clippy"), #[clippy::version = "pre 1.29.0"] ("clippy::extend_from_slice", "`Vec::extend_from_slice` is no longer faster than `Vec::extend` due to specialization"), - #[clippy::version = "1.86.0"] + #[clippy::version = "1.88.0"] ("clippy::match_on_vec_items", "`clippy::indexing_slicing` covers indexing and slicing on `Vec<_>`"), #[clippy::version = "pre 1.29.0"] ("clippy::misaligned_transmute", "split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr`"), - #[clippy::version = "1.86.0"] + #[clippy::version = "1.87.0"] ("clippy::option_map_or_err_ok", "`clippy::manual_ok_or` covers this case"), #[clippy::version = "1.54.0"] ("clippy::pub_enum_variant_names", "`clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config"), @@ -34,7 +34,7 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [ ("clippy::replace_consts", "`min_value` and `max_value` are now deprecated"), #[clippy::version = "pre 1.29.0"] ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"), - #[clippy::version = "1.90.0"] + #[clippy::version = "1.91.0"] ("clippy::string_to_string", "`clippy:implicit_clone` covers those cases"), #[clippy::version = "pre 1.29.0"] ("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"), @@ -184,6 +184,8 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ ("clippy::transmute_int_to_float", "unnecessary_transmutes"), #[clippy::version = "1.88.0"] ("clippy::transmute_num_to_bytes", "unnecessary_transmutes"), + #[clippy::version = "1.90.0"] + ("clippy::unchecked_duration_subtraction", "clippy::unchecked_time_subtraction"), #[clippy::version = ""] ("clippy::undropped_manually_drops", "undropped_manually_drops"), #[clippy::version = ""] diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index a70105db19490..9ebb8e6e15d9e 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -3,7 +3,8 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop, peel_and_count_ty_refs}; use clippy_utils::{ - DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, + DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, + path_to_local, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; @@ -260,6 +261,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { }; self.skip_expr = skip_expr; + if is_from_proc_macro(cx, expr) { + if let Some((state, data)) = self.state.take() { + report(cx, expr, state, data, cx.typeck_results()); + } + return; + } + match (self.state.take(), kind) { (None, kind) => { let expr_ty = typeck.expr_ty(expr); diff --git a/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs b/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs index cbbcb2f7a3bab..274c699ff9d24 100644 --- a/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs +++ b/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs @@ -1,5 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use rustc_hir as hir; +use clippy_utils::diagnostics::span_lint_hir_and_then; +use rustc_hir::{self as hir, HirId}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::{Span, sym}; @@ -12,6 +12,7 @@ pub(super) fn check<'tcx>( span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>, + adt_hir_id: HirId, ord_is_automatically_derived: bool, ) { if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) @@ -38,7 +39,7 @@ pub(super) fn check<'tcx>( "you are deriving `Ord` but have implemented `PartialOrd` explicitly" }; - span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| { + span_lint_hir_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, adt_hir_id, span, mess, |diag| { if let Some(local_def_id) = impl_id.as_local() { let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); diag.span_note(cx.tcx.hir_span(hir_id), "`PartialOrd` implemented here"); diff --git a/clippy_lints/src/derive/derive_partial_eq_without_eq.rs b/clippy_lints/src/derive/derive_partial_eq_without_eq.rs index ed7881c461ffc..fbace0bd73acf 100644 --- a/clippy_lints/src/derive/derive_partial_eq_without_eq.rs +++ b/clippy_lints/src/derive/derive_partial_eq_without_eq.rs @@ -2,8 +2,8 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::has_non_exhaustive_attr; use clippy_utils::ty::implements_trait_with_env; use rustc_errors::Applicability; -use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, HirId}; use rustc_lint::LateContext; use rustc_middle::ty::{self, ClauseKind, GenericParamDefKind, ParamEnv, TraitPredicate, Ty, TyCtxt, Upcast}; use rustc_span::{Span, sym}; @@ -11,7 +11,13 @@ use rustc_span::{Span, sym}; use super::DERIVE_PARTIAL_EQ_WITHOUT_EQ; /// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + span: Span, + trait_ref: &hir::TraitRef<'_>, + ty: Ty<'tcx>, + adt_hir_id: HirId, +) { if let ty::Adt(adt, args) = ty.kind() && cx.tcx.visibility(adt.did()).is_public() && let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq) @@ -20,7 +26,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::T && !has_non_exhaustive_attr(cx.tcx, *adt) && !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id) && let typing_env = typing_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) - && let Some(local_def_id) = adt.did().as_local() // If all of our fields implement `Eq`, we can implement `Eq` too && adt .all_fields() @@ -30,7 +35,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::T span_lint_hir_and_then( cx, DERIVE_PARTIAL_EQ_WITHOUT_EQ, - cx.tcx.local_def_id_to_hir_id(local_def_id), + adt_hir_id, span.ctxt().outer_expn_data().call_site, "you are deriving `PartialEq` and can implement `Eq`", |diag| { diff --git a/clippy_lints/src/derive/derived_hash_with_manual_eq.rs b/clippy_lints/src/derive/derived_hash_with_manual_eq.rs index 6f36a58025a2b..afc02ce32d48e 100644 --- a/clippy_lints/src/derive/derived_hash_with_manual_eq.rs +++ b/clippy_lints/src/derive/derived_hash_with_manual_eq.rs @@ -1,5 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use rustc_hir as hir; +use clippy_utils::diagnostics::span_lint_hir_and_then; +use rustc_hir::{HirId, TraitRef}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::{Span, sym}; @@ -10,8 +10,9 @@ use super::DERIVED_HASH_WITH_MANUAL_EQ; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, span: Span, - trait_ref: &hir::TraitRef<'_>, + trait_ref: &TraitRef<'_>, ty: Ty<'tcx>, + adt_hir_id: HirId, hash_is_automatically_derived: bool, ) { if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait() @@ -31,9 +32,10 @@ pub(super) fn check<'tcx>( // Only care about `impl PartialEq for Foo` // For `impl PartialEq for A, input_types is [A, B] if trait_ref.instantiate_identity().args.type_at(1) == ty { - span_lint_and_then( + span_lint_hir_and_then( cx, DERIVED_HASH_WITH_MANUAL_EQ, + adt_hir_id, span, "you are deriving `Hash` but have implemented `PartialEq` explicitly", |diag| { diff --git a/clippy_lints/src/derive/expl_impl_clone_on_copy.rs b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs index 6b97b4bd6b4d3..dfb723b86eb93 100644 --- a/clippy_lints/src/derive/expl_impl_clone_on_copy.rs +++ b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs @@ -1,13 +1,19 @@ -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::{implements_trait, is_copy}; -use rustc_hir::{self as hir, Item}; +use rustc_hir::{self as hir, HirId, Item}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArgKind, Ty}; use super::EXPL_IMPL_CLONE_ON_COPY; /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + item: &Item<'_>, + trait_ref: &hir::TraitRef<'_>, + ty: Ty<'tcx>, + adt_hir_id: HirId, +) { let clone_id = match cx.tcx.lang_items().clone_trait() { Some(id) if trait_ref.trait_def_id() == Some(id) => id, _ => return, @@ -54,12 +60,14 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h return; } - span_lint_and_note( + span_lint_hir_and_then( cx, EXPL_IMPL_CLONE_ON_COPY, + adt_hir_id, item.span, "you are implementing `Clone` explicitly on a `Copy` type", - Some(item.span), - "consider deriving `Clone` or removing `Copy`", + |diag| { + diag.span_help(item.span, "consider deriving `Clone` or removing `Copy`"); + }, ); } diff --git a/clippy_lints/src/derive/mod.rs b/clippy_lints/src/derive/mod.rs index 1d63394ce37d4..06efc2709faa6 100644 --- a/clippy_lints/src/derive/mod.rs +++ b/clippy_lints/src/derive/mod.rs @@ -1,3 +1,5 @@ +use clippy_utils::path_res; +use rustc_hir::def::Res; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -194,21 +196,25 @@ impl<'tcx> LateLintPass<'tcx> for Derive { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { of_trait: Some(of_trait), + self_ty, .. }) = item.kind + && let Res::Def(_, def_id) = path_res(cx, self_ty) + && let Some(local_def_id) = def_id.as_local() { + let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); let trait_ref = &of_trait.trait_ref; let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id()); - derived_hash_with_manual_eq::check(cx, item.span, trait_ref, ty, is_automatically_derived); - derive_ord_xor_partial_ord::check(cx, item.span, trait_ref, ty, is_automatically_derived); + derived_hash_with_manual_eq::check(cx, item.span, trait_ref, ty, adt_hir_id, is_automatically_derived); + derive_ord_xor_partial_ord::check(cx, item.span, trait_ref, ty, adt_hir_id, is_automatically_derived); if is_automatically_derived { - unsafe_derive_deserialize::check(cx, item, trait_ref, ty); - derive_partial_eq_without_eq::check(cx, item.span, trait_ref, ty); + unsafe_derive_deserialize::check(cx, item, trait_ref, ty, adt_hir_id); + derive_partial_eq_without_eq::check(cx, item.span, trait_ref, ty, adt_hir_id); } else { - expl_impl_clone_on_copy::check(cx, item, trait_ref, ty); + expl_impl_clone_on_copy::check(cx, item, trait_ref, ty, adt_hir_id); } } } diff --git a/clippy_lints/src/derive/unsafe_derive_deserialize.rs b/clippy_lints/src/derive/unsafe_derive_deserialize.rs index c391e7b62289f..38f3251fd3891 100644 --- a/clippy_lints/src/derive/unsafe_derive_deserialize.rs +++ b/clippy_lints/src/derive/unsafe_derive_deserialize.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::{is_lint_allowed, paths}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item}; -use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Item, UnsafeSource}; +use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Item, UnsafeSource}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; @@ -13,7 +13,13 @@ use rustc_span::{Span, sym}; use super::UNSAFE_DERIVE_DESERIALIZE; /// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + item: &Item<'_>, + trait_ref: &hir::TraitRef<'_>, + ty: Ty<'tcx>, + adt_hir_id: HirId, +) { fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool { let mut visitor = UnsafeVisitor { cx }; walk_item(&mut visitor, item).is_break() @@ -22,8 +28,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h if let Some(trait_def_id) = trait_ref.trait_def_id() && paths::SERDE_DESERIALIZE.matches(cx, trait_def_id) && let ty::Adt(def, _) = ty.kind() - && let Some(local_def_id) = def.did().as_local() - && let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id) && !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id) && cx .tcx diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index eca3bc390d778..f8ae770b3a4db 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -315,7 +315,7 @@ declare_clippy_lint! { /// /// [example of a good link](https://github.com/rust-lang/rust-clippy/) /// pub fn do_something() {} /// ``` - #[clippy::version = "1.84.0"] + #[clippy::version = "1.90.0"] pub DOC_BROKEN_LINK, pedantic, "broken document link" diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index 4dd8f01ee7090..bddf4702fb340 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -1,5 +1,7 @@ -use clippy_utils::diagnostics::span_lint; -use rustc_ast::ast::{Expr, ExprKind}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{HasSession, snippet_with_applicability, snippet_with_context}; +use rustc_ast::ast::{Expr, ExprKind, MethodCall}; +use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; @@ -24,7 +26,7 @@ declare_clippy_lint! { /// Use instead: /// ```no_run /// fn simple_no_parens() -> i32 { - /// 0 + /// (0) /// } /// /// # fn foo(bar: usize) {} @@ -40,29 +42,54 @@ declare_lint_pass!(DoubleParens => [DOUBLE_PARENS]); impl EarlyLintPass for DoubleParens { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - let span = match &expr.kind { - ExprKind::Paren(in_paren) if matches!(in_paren.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => expr.span, - ExprKind::Call(_, params) - if let [param] = &**params - && let ExprKind::Paren(_) = param.kind => - { - param.span + match &expr.kind { + // ((..)) + // ^^^^^^ expr + // ^^^^ inner + ExprKind::Paren(inner) if matches!(inner.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => { + // suggest removing the outer parens + if expr.span.eq_ctxt(inner.span) { + let mut applicability = Applicability::MachineApplicable; + // We don't need to use `snippet_with_context` here, because: + // - if `inner`'s `ctxt` is from macro, we don't lint in the first place (see the check above) + // - otherwise, calling `snippet_with_applicability` on a not-from-macro span is fine + let sugg = snippet_with_applicability(cx.sess(), inner.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + DOUBLE_PARENS, + expr.span, + "unnecessary parentheses", + "remove them", + sugg.to_string(), + applicability, + ); + } }, - ExprKind::MethodCall(call) - if let [arg] = &*call.args - && let ExprKind::Paren(_) = arg.kind => + + // func((n)) + // ^^^^^^^^^ expr + // ^^^ arg + // ^ inner + ExprKind::Call(_, args) | ExprKind::MethodCall(box MethodCall { args, .. }) + if let [arg] = &**args + && let ExprKind::Paren(inner) = &arg.kind => { - arg.span + // suggest removing the inner parens + if expr.span.eq_ctxt(arg.span) { + let mut applicability = Applicability::MachineApplicable; + let sugg = snippet_with_context(cx.sess(), inner.span, arg.span.ctxt(), "_", &mut applicability).0; + span_lint_and_sugg( + cx, + DOUBLE_PARENS, + arg.span, + "unnecessary parentheses", + "remove them", + sugg.to_string(), + applicability, + ); + } }, - _ => return, - }; - if !expr.span.from_expansion() { - span_lint( - cx, - DOUBLE_PARENS, - span, - "consider removing unnecessary double parentheses", - ); + _ => {}, } } } diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index c828fc57f7601..1a56c8f810ee7 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -43,12 +43,8 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { if let Some(anon_const) = &var.disr_expr { let def_id = cx.tcx.hir_body_owner_def_id(anon_const.body); let mut ty = cx.tcx.type_of(def_id.to_def_id()).instantiate_identity(); - let constant = cx - .tcx - .const_eval_poly(def_id.to_def_id()) - .ok() - .map(|val| rustc_middle::mir::Const::from_value(val, ty)); - if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c)) { + let constant = cx.tcx.const_eval_poly(def_id.to_def_id()).ok(); + if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c, ty)) { if let ty::Adt(adt, _) = ty.kind() && adt.is_enum() { diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 84d39dd81c919..407a3f1306739 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -11,6 +11,7 @@ use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; +use rustc_span::SyntaxContext; use rustc_span::source_map::Spanned; use std::f32::consts as f32_consts; use std::f64::consts as f64_consts; @@ -110,8 +111,8 @@ declare_lint_pass!(FloatingPointArithmetic => [ // Returns the specialized log method for a given base if base is constant // and is one of 2, 10 and e -fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> { - if let Some(value) = ConstEvalCtxt::new(cx).eval(base) { +fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>, ctxt: SyntaxContext) -> Option<&'static str> { + if let Some(value) = ConstEvalCtxt::new(cx).eval_local(base, ctxt) { if F32(2.0) == value || F64(2.0) == value { return Some("log2"); } else if F32(10.0) == value || F64(10.0) == value { @@ -157,7 +158,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su } fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { - if let Some(method) = get_specialized_log_method(cx, &args[0]) { + if let Some(method) = get_specialized_log_method(cx, &args[0], expr.span.ctxt()) { span_lint_and_sugg( cx, SUBOPTIMAL_FLOPS, @@ -205,7 +206,7 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) { // ranges [-16777215, 16777216) for type f32 as whole number floats outside // this range are lossy and ambiguous. #[expect(clippy::cast_possible_truncation)] -fn get_integer_from_float_constant(value: &Constant<'_>) -> Option { +fn get_integer_from_float_constant(value: &Constant) -> Option { match value { F32(num) if num.fract() == 0.0 => { if (-16_777_215.0..16_777_216.0).contains(num) { @@ -517,8 +518,8 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool { if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind { match op { - BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && eq_expr_value(cx, left, test), - BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && eq_expr_value(cx, right, test), + BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, left, test), + BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, right, test), _ => false, } } else { @@ -530,8 +531,8 @@ fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool { if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind { match op { - BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && eq_expr_value(cx, right, test), - BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && eq_expr_value(cx, left, test), + BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, right, test), + BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, left, test), _ => false, } } else { @@ -540,8 +541,8 @@ fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - } /// Returns true iff expr is some zero literal -fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match ConstEvalCtxt::new(cx).eval_simple(expr) { +fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> bool { + match ConstEvalCtxt::new(cx).eval_local(expr, ctxt) { Some(Int(i)) => i == 0, Some(F32(f)) => f == 0.0, Some(F64(f)) => f == 0.0, diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs index e8afa69b537ea..54e9538fcb993 100644 --- a/clippy_lints/src/if_not_else.rs +++ b/clippy_lints/src/if_not_else.rs @@ -60,10 +60,14 @@ impl LateLintPass<'_> for IfNotElse { ), // Don't lint on `… != 0`, as these are likely to be bit tests. // For example, `if foo & 0x0F00 != 0 { … } else { … }` is already in the "proper" order. - ExprKind::Binary(op, _, rhs) if op.node == BinOpKind::Ne && !is_zero_integer_const(cx, rhs) => ( - "unnecessary `!=` operation", - "change to `==` and swap the blocks of the `if`/`else`", - ), + ExprKind::Binary(op, _, rhs) + if op.node == BinOpKind::Ne && !is_zero_integer_const(cx, rhs, e.span.ctxt()) => + { + ( + "unnecessary `!=` operation", + "change to `==` and swap the blocks of the `if`/`else`", + ) + }, _ => return, }; diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index b50d91f101463..f9fee292837ea 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -79,6 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && !is_in_const_context(cx) && self.msrv.meets(cx, msrvs::BOOL_THEN) && !contains_return(then_block.stmts) + && then_block.expr.is_none_or(|expr| !contains_return(expr)) { let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) { sym::then_some diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/ifs/branches_sharing_code.rs similarity index 63% rename from clippy_lints/src/copies.rs rename to clippy_lints/src/ifs/branches_sharing_code.rs index 4fdb497950f8d..eb1025f71498e 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/ifs/branches_sharing_code.rs @@ -1,218 +1,23 @@ -use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint, span_lint_and_note, span_lint_and_then}; -use clippy_utils::higher::has_let_expr; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet}; -use clippy_utils::ty::{InteriorMut, needs_ordered_drop}; +use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{ - ContainsName, HirEqInterExpr, SpanlessEq, capture_local_usage, eq_expr_value, find_binding_init, - get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause, is_lint_allowed, path_to_local, - search_same, + ContainsName, HirEqInterExpr, SpanlessEq, capture_local_usage, get_enclosing_block, hash_expr, hash_stmt, + path_to_local, }; use core::iter; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Node, Stmt, StmtKind, intravisit}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::TyCtxt; -use rustc_session::impl_lint_pass; +use rustc_lint::LateContext; use rustc_span::hygiene::walk_chain; use rustc_span::source_map::SourceMap; use rustc_span::{Span, Symbol}; -declare_clippy_lint! { - /// ### What it does - /// Checks for consecutive `if`s with the same condition. - /// - /// ### Why is this bad? - /// This is probably a copy & paste error. - /// - /// ### Example - /// ```ignore - /// if a == b { - /// … - /// } else if a == b { - /// … - /// } - /// ``` - /// - /// Note that this lint ignores all conditions with a function call as it could - /// have side effects: - /// - /// ```ignore - /// if foo() { - /// … - /// } else if foo() { // not linted - /// … - /// } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub IFS_SAME_COND, - correctness, - "consecutive `if`s with the same condition" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for consecutive `if`s with the same function call. - /// - /// ### Why is this bad? - /// This is probably a copy & paste error. - /// Despite the fact that function can have side effects and `if` works as - /// intended, such an approach is implicit and can be considered a "code smell". - /// - /// ### Example - /// ```ignore - /// if foo() == bar { - /// … - /// } else if foo() == bar { - /// … - /// } - /// ``` - /// - /// This probably should be: - /// ```ignore - /// if foo() == bar { - /// … - /// } else if foo() == baz { - /// … - /// } - /// ``` - /// - /// or if the original code was not a typo and called function mutates a state, - /// consider move the mutation out of the `if` condition to avoid similarity to - /// a copy & paste error: - /// - /// ```ignore - /// let first = foo(); - /// if first == bar { - /// … - /// } else { - /// let second = foo(); - /// if second == bar { - /// … - /// } - /// } - /// ``` - #[clippy::version = "1.41.0"] - pub SAME_FUNCTIONS_IN_IF_CONDITION, - pedantic, - "consecutive `if`s with the same function call" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for `if/else` with the same body as the *then* part - /// and the *else* part. - /// - /// ### Why is this bad? - /// This is probably a copy & paste error. - /// - /// ### Example - /// ```ignore - /// let foo = if … { - /// 42 - /// } else { - /// 42 - /// }; - /// ``` - #[clippy::version = "pre 1.29.0"] - pub IF_SAME_THEN_ELSE, - style, - "`if` with the same `then` and `else` blocks" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks if the `if` and `else` block contain shared code that can be - /// moved out of the blocks. - /// - /// ### Why is this bad? - /// Duplicate code is less maintainable. - /// - /// ### Example - /// ```ignore - /// let foo = if … { - /// println!("Hello World"); - /// 13 - /// } else { - /// println!("Hello World"); - /// 42 - /// }; - /// ``` - /// - /// Use instead: - /// ```ignore - /// println!("Hello World"); - /// let foo = if … { - /// 13 - /// } else { - /// 42 - /// }; - /// ``` - #[clippy::version = "1.53.0"] - pub BRANCHES_SHARING_CODE, - nursery, - "`if` statement with shared code in all blocks" -} - -pub struct CopyAndPaste<'tcx> { - interior_mut: InteriorMut<'tcx>, -} - -impl<'tcx> CopyAndPaste<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self { - Self { - interior_mut: InteriorMut::new(tcx, &conf.ignore_interior_mutability), - } - } -} - -impl_lint_pass!(CopyAndPaste<'_> => [ - IFS_SAME_COND, - SAME_FUNCTIONS_IN_IF_CONDITION, - IF_SAME_THEN_ELSE, - BRANCHES_SHARING_CODE -]); - -impl<'tcx> LateLintPass<'tcx> for CopyAndPaste<'tcx> { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) { - let (conds, blocks) = if_sequence(expr); - lint_same_cond(cx, &conds, &mut self.interior_mut); - lint_same_fns_in_if_cond(cx, &conds); - let all_same = - !is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && lint_if_same_then_else(cx, &conds, &blocks); - if !all_same && conds.len() != blocks.len() { - lint_branches_sharing_code(cx, &conds, &blocks, expr); - } - } - } -} - -fn lint_if_same_then_else(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&Block<'_>]) -> bool { - let mut eq = SpanlessEq::new(cx); - blocks - .array_windows::<2>() - .enumerate() - .fold(true, |all_eq, (i, &[lhs, rhs])| { - if eq.eq_block(lhs, rhs) && !has_let_expr(conds[i]) && conds.get(i + 1).is_none_or(|e| !has_let_expr(e)) { - span_lint_and_note( - cx, - IF_SAME_THEN_ELSE, - lhs.span, - "this `if` has identical blocks", - Some(rhs.span), - "same as this", - ); - all_eq - } else { - false - } - }) -} +use super::BRANCHES_SHARING_CODE; -fn lint_branches_sharing_code<'tcx>( +pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, conds: &[&'tcx Expr<'_>], blocks: &[&'tcx Block<'_>], @@ -356,8 +161,8 @@ fn modifies_any_local<'tcx>(cx: &LateContext<'tcx>, s: &'tcx Stmt<'_>, locals: & .is_some() } -/// Checks if the given statement should be considered equal to the statement in the same position -/// for each block. +/// Checks if the given statement should be considered equal to the statement in the same +/// position for each block. fn eq_stmts( stmt: &Stmt<'_>, blocks: &[&Block<'_>], @@ -516,9 +321,9 @@ fn scan_block_for_eq<'tcx>( } } -/// Adjusts the index for which the statements begin to differ to the closest macro callsite. This -/// avoids giving suggestions that requires splitting a macro call in half, when only a part of the -/// macro expansion is equal. +/// Adjusts the index for which the statements begin to differ to the closest macro callsite. +/// This avoids giving suggestions that requires splitting a macro call in half, when only a +/// part of the macro expansion is equal. /// /// For example, for the following macro: /// ```rust,ignore @@ -587,70 +392,6 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo }) } -fn method_caller_is_mutable<'tcx>( - cx: &LateContext<'tcx>, - caller_expr: &Expr<'_>, - interior_mut: &mut InteriorMut<'tcx>, -) -> bool { - let caller_ty = cx.typeck_results().expr_ty(caller_expr); - - interior_mut.is_interior_mut_ty(cx, caller_ty) - || caller_ty.is_mutable_ptr() - // `find_binding_init` will return the binding iff its not mutable - || path_to_local(caller_expr) - .and_then(|hid| find_binding_init(cx, hid)) - .is_none() -} - -/// Implementation of `IFS_SAME_COND`. -fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mut: &mut InteriorMut<'tcx>) { - for group in search_same( - conds, - |e| hash_expr(cx, e), - |lhs, rhs| { - // Ignore eq_expr side effects iff one of the expression kind is a method call - // and the caller is not a mutable, including inner mutable type. - if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind { - if method_caller_is_mutable(cx, caller, interior_mut) { - false - } else { - SpanlessEq::new(cx).eq_expr(lhs, rhs) - } - } else { - eq_expr_value(cx, lhs, rhs) - } - }, - ) { - let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); - span_lint(cx, IFS_SAME_COND, spans, "these `if` branches have the same condition"); - } -} - -/// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`. -fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { - let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { - // Do not lint if any expr originates from a macro - if lhs.span.from_expansion() || rhs.span.from_expansion() { - return false; - } - // Do not spawn warning if `IFS_SAME_COND` already produced it. - if eq_expr_value(cx, lhs, rhs) { - return false; - } - SpanlessEq::new(cx).eq_expr(lhs, rhs) - }; - - for group in search_same(conds, |e| hash_expr(cx, e), eq) { - let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); - span_lint( - cx, - SAME_FUNCTIONS_IN_IF_CONDITION, - spans, - "these `if` branches have the same function call", - ); - } -} - fn is_expr_parent_assignment(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let parent = cx.tcx.parent_hir_node(expr.hir_id); if let Node::LetStmt(LetStmt { init: Some(e), .. }) diff --git a/clippy_lints/src/ifs/if_same_then_else.rs b/clippy_lints/src/ifs/if_same_then_else.rs new file mode 100644 index 0000000000000..69402ec890768 --- /dev/null +++ b/clippy_lints/src/ifs/if_same_then_else.rs @@ -0,0 +1,29 @@ +use clippy_utils::SpanlessEq; +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::higher::has_let_expr; +use rustc_hir::{Block, Expr}; +use rustc_lint::LateContext; + +use super::IF_SAME_THEN_ELSE; + +pub(super) fn check(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&Block<'_>]) -> bool { + let mut eq = SpanlessEq::new(cx); + blocks + .array_windows::<2>() + .enumerate() + .fold(true, |all_eq, (i, &[lhs, rhs])| { + if eq.eq_block(lhs, rhs) && !has_let_expr(conds[i]) && conds.get(i + 1).is_none_or(|e| !has_let_expr(e)) { + span_lint_and_note( + cx, + IF_SAME_THEN_ELSE, + lhs.span, + "this `if` has identical blocks", + Some(rhs.span), + "same as this", + ); + all_eq + } else { + false + } + }) +} diff --git a/clippy_lints/src/ifs/ifs_same_cond.rs b/clippy_lints/src/ifs/ifs_same_cond.rs new file mode 100644 index 0000000000000..ca76fc2587db9 --- /dev/null +++ b/clippy_lints/src/ifs/ifs_same_cond.rs @@ -0,0 +1,46 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::InteriorMut; +use clippy_utils::{SpanlessEq, eq_expr_value, find_binding_init, hash_expr, path_to_local, search_same}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; + +use super::IFS_SAME_COND; + +fn method_caller_is_mutable<'tcx>( + cx: &LateContext<'tcx>, + caller_expr: &Expr<'_>, + interior_mut: &mut InteriorMut<'tcx>, +) -> bool { + let caller_ty = cx.typeck_results().expr_ty(caller_expr); + + interior_mut.is_interior_mut_ty(cx, caller_ty) + || caller_ty.is_mutable_ptr() + // `find_binding_init` will return the binding iff its not mutable + || path_to_local(caller_expr) + .and_then(|hid| find_binding_init(cx, hid)) + .is_none() +} + +/// Implementation of `IFS_SAME_COND`. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mut: &mut InteriorMut<'tcx>) { + for group in search_same( + conds, + |e| hash_expr(cx, e), + |lhs, rhs| { + // Ignore eq_expr side effects iff one of the expression kind is a method call + // and the caller is not a mutable, including inner mutable type. + if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind { + if method_caller_is_mutable(cx, caller, interior_mut) { + false + } else { + SpanlessEq::new(cx).eq_expr(lhs, rhs) + } + } else { + eq_expr_value(cx, lhs, rhs) + } + }, + ) { + let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); + span_lint(cx, IFS_SAME_COND, spans, "these `if` branches have the same condition"); + } +} diff --git a/clippy_lints/src/ifs/mod.rs b/clippy_lints/src/ifs/mod.rs new file mode 100644 index 0000000000000..739f2fc917293 --- /dev/null +++ b/clippy_lints/src/ifs/mod.rs @@ -0,0 +1,182 @@ +use clippy_config::Conf; +use clippy_utils::ty::InteriorMut; +use clippy_utils::{if_sequence, is_else_clause, is_lint_allowed}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; +use rustc_session::impl_lint_pass; + +mod branches_sharing_code; +mod if_same_then_else; +mod ifs_same_cond; +mod same_functions_in_if_cond; + +declare_clippy_lint! { + /// ### What it does + /// Checks for consecutive `if`s with the same condition. + /// + /// ### Why is this bad? + /// This is probably a copy & paste error. + /// + /// ### Example + /// ```ignore + /// if a == b { + /// … + /// } else if a == b { + /// … + /// } + /// ``` + /// + /// Note that this lint ignores all conditions with a function call as it could + /// have side effects: + /// + /// ```ignore + /// if foo() { + /// … + /// } else if foo() { // not linted + /// … + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub IFS_SAME_COND, + correctness, + "consecutive `if`s with the same condition" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for consecutive `if`s with the same function call. + /// + /// ### Why is this bad? + /// This is probably a copy & paste error. + /// Despite the fact that function can have side effects and `if` works as + /// intended, such an approach is implicit and can be considered a "code smell". + /// + /// ### Example + /// ```ignore + /// if foo() == bar { + /// … + /// } else if foo() == bar { + /// … + /// } + /// ``` + /// + /// This probably should be: + /// ```ignore + /// if foo() == bar { + /// … + /// } else if foo() == baz { + /// … + /// } + /// ``` + /// + /// or if the original code was not a typo and called function mutates a state, + /// consider move the mutation out of the `if` condition to avoid similarity to + /// a copy & paste error: + /// + /// ```ignore + /// let first = foo(); + /// if first == bar { + /// … + /// } else { + /// let second = foo(); + /// if second == bar { + /// … + /// } + /// } + /// ``` + #[clippy::version = "1.41.0"] + pub SAME_FUNCTIONS_IN_IF_CONDITION, + pedantic, + "consecutive `if`s with the same function call" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `if/else` with the same body as the *then* part + /// and the *else* part. + /// + /// ### Why is this bad? + /// This is probably a copy & paste error. + /// + /// ### Example + /// ```ignore + /// let foo = if … { + /// 42 + /// } else { + /// 42 + /// }; + /// ``` + #[clippy::version = "pre 1.29.0"] + pub IF_SAME_THEN_ELSE, + style, + "`if` with the same `then` and `else` blocks" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks if the `if` and `else` block contain shared code that can be + /// moved out of the blocks. + /// + /// ### Why is this bad? + /// Duplicate code is less maintainable. + /// + /// ### Example + /// ```ignore + /// let foo = if … { + /// println!("Hello World"); + /// 13 + /// } else { + /// println!("Hello World"); + /// 42 + /// }; + /// ``` + /// + /// Use instead: + /// ```ignore + /// println!("Hello World"); + /// let foo = if … { + /// 13 + /// } else { + /// 42 + /// }; + /// ``` + #[clippy::version = "1.53.0"] + pub BRANCHES_SHARING_CODE, + nursery, + "`if` statement with shared code in all blocks" +} + +pub struct CopyAndPaste<'tcx> { + interior_mut: InteriorMut<'tcx>, +} + +impl<'tcx> CopyAndPaste<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self { + Self { + interior_mut: InteriorMut::new(tcx, &conf.ignore_interior_mutability), + } + } +} + +impl_lint_pass!(CopyAndPaste<'_> => [ + IFS_SAME_COND, + SAME_FUNCTIONS_IN_IF_CONDITION, + IF_SAME_THEN_ELSE, + BRANCHES_SHARING_CODE +]); + +impl<'tcx> LateLintPass<'tcx> for CopyAndPaste<'tcx> { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) { + let (conds, blocks) = if_sequence(expr); + ifs_same_cond::check(cx, &conds, &mut self.interior_mut); + same_functions_in_if_cond::check(cx, &conds); + let all_same = + !is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && if_same_then_else::check(cx, &conds, &blocks); + if !all_same && conds.len() != blocks.len() { + branches_sharing_code::check(cx, &conds, &blocks, expr); + } + } + } +} diff --git a/clippy_lints/src/ifs/same_functions_in_if_cond.rs b/clippy_lints/src/ifs/same_functions_in_if_cond.rs new file mode 100644 index 0000000000000..1f6bf04e22e7d --- /dev/null +++ b/clippy_lints/src/ifs/same_functions_in_if_cond.rs @@ -0,0 +1,31 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{SpanlessEq, eq_expr_value, hash_expr, search_same}; +use rustc_hir::Expr; +use rustc_lint::LateContext; + +use super::SAME_FUNCTIONS_IN_IF_CONDITION; + +/// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`. +pub(super) fn check(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { + let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { + // Do not lint if any expr originates from a macro + if lhs.span.from_expansion() || rhs.span.from_expansion() { + return false; + } + // Do not spawn warning if `IFS_SAME_COND` already produced it. + if eq_expr_value(cx, lhs, rhs) { + return false; + } + SpanlessEq::new(cx).eq_expr(lhs, rhs) + }; + + for group in search_same(conds, |e| hash_expr(cx, e), eq) { + let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect(); + span_lint( + cx, + SAME_FUNCTIONS_IN_IF_CONDITION, + spans, + "these `if` branches have the same function call", + ); + } +} diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 076017a247b4b..6ed478b2708a3 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{desugar_await, get_async_closure_expr, get_async_fn_body, is_async_fn, is_from_proc_macro}; +use clippy_utils::{desugar_await, get_async_closure_expr, get_async_fn_body, is_from_proc_macro}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; @@ -240,7 +240,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn { return; } - let expr = if is_async_fn(kind) { + let expr = if kind.asyncness().is_async() { match get_async_fn_body(cx.tcx, body) { Some(e) => e, None => return, diff --git a/clippy_lints/src/implicit_saturating_add.rs b/clippy_lints/src/implicit_saturating_add.rs index 0fdbf67973813..4bf3a390b050c 100644 --- a/clippy_lints/src/implicit_saturating_add.rs +++ b/clippy_lints/src/implicit_saturating_add.rs @@ -117,10 +117,11 @@ fn get_int_max(ty: Ty<'_>) -> Option { fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, BinOpKind, &'tcx Expr<'tcx>)> { if let ExprKind::Binary(op, l, r) = expr.kind { let ecx = ConstEvalCtxt::new(cx); - if let Some(Constant::Int(c)) = ecx.eval(r) { + let ctxt = expr.span.ctxt(); + if let Some(Constant::Int(c)) = ecx.eval_local(r, ctxt) { return Some((c, op.node, l)); } - if let Some(Constant::Int(c)) = ecx.eval(l) { + if let Some(Constant::Int(c)) = ecx.eval_local(l, ctxt) { return Some((c, invert_op(op.node)?, r)); } } diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs deleted file mode 100644 index 13117f60abd52..0000000000000 --- a/clippy_lints/src/instant_subtraction.rs +++ /dev/null @@ -1,151 +0,0 @@ -use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_context; -use clippy_utils::sugg::Sugg; -use clippy_utils::{is_path_diagnostic_item, ty}; -use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::impl_lint_pass; -use rustc_span::source_map::Spanned; -use rustc_span::sym; - -declare_clippy_lint! { - /// ### What it does - /// Lints subtraction between `Instant::now()` and another `Instant`. - /// - /// ### Why is this bad? - /// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns - /// as `Instant` subtraction saturates. - /// - /// `prev_instant.elapsed()` also more clearly signals intention. - /// - /// ### Example - /// ```no_run - /// use std::time::Instant; - /// let prev_instant = Instant::now(); - /// let duration = Instant::now() - prev_instant; - /// ``` - /// Use instead: - /// ```no_run - /// use std::time::Instant; - /// let prev_instant = Instant::now(); - /// let duration = prev_instant.elapsed(); - /// ``` - #[clippy::version = "1.65.0"] - pub MANUAL_INSTANT_ELAPSED, - pedantic, - "subtraction between `Instant::now()` and previous `Instant`" -} - -declare_clippy_lint! { - /// ### What it does - /// Lints subtraction between an `Instant` and a `Duration`. - /// - /// ### Why is this bad? - /// Unchecked subtraction could cause underflow on certain platforms, leading to - /// unintentional panics. - /// - /// ### Example - /// ```no_run - /// # use std::time::{Instant, Duration}; - /// let time_passed = Instant::now() - Duration::from_secs(5); - /// ``` - /// - /// Use instead: - /// ```no_run - /// # use std::time::{Instant, Duration}; - /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); - /// ``` - #[clippy::version = "1.67.0"] - pub UNCHECKED_DURATION_SUBTRACTION, - pedantic, - "finds unchecked subtraction of a 'Duration' from an 'Instant'" -} - -pub struct InstantSubtraction { - msrv: Msrv, -} - -impl InstantSubtraction { - pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } - } -} - -impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_DURATION_SUBTRACTION]); - -impl LateLintPass<'_> for InstantSubtraction { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { - if let ExprKind::Binary( - Spanned { - node: BinOpKind::Sub, .. - }, - lhs, - rhs, - ) = expr.kind - && let typeck = cx.typeck_results() - && ty::is_type_diagnostic_item(cx, typeck.expr_ty(lhs), sym::Instant) - { - let rhs_ty = typeck.expr_ty(rhs); - - if is_instant_now_call(cx, lhs) - && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Instant) - && let Some(sugg) = Sugg::hir_opt(cx, rhs) - { - print_manual_instant_elapsed_sugg(cx, expr, sugg); - } else if ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) - && !expr.span.from_expansion() - && self.msrv.meets(cx, msrvs::TRY_FROM) - { - print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); - } - } - } -} - -fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { - if let ExprKind::Call(fn_expr, []) = expr_block.kind - && is_path_diagnostic_item(cx, fn_expr, sym::instant_now) - { - true - } else { - false - } -} - -fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) { - span_lint_and_sugg( - cx, - MANUAL_INSTANT_ELAPSED, - expr.span, - "manual implementation of `Instant::elapsed`", - "try", - format!("{}.elapsed()", sugg.maybe_paren()), - Applicability::MachineApplicable, - ); -} - -fn print_unchecked_duration_subtraction_sugg( - cx: &LateContext<'_>, - left_expr: &Expr<'_>, - right_expr: &Expr<'_>, - expr: &Expr<'_>, -) { - let mut applicability = Applicability::MachineApplicable; - - let ctxt = expr.span.ctxt(); - let left_expr = snippet_with_context(cx, left_expr.span, ctxt, "", &mut applicability).0; - let right_expr = snippet_with_context(cx, right_expr.span, ctxt, "", &mut applicability).0; - - span_lint_and_sugg( - cx, - UNCHECKED_DURATION_SUBTRACTION, - expr.span, - "unchecked subtraction of a 'Duration' from an 'Instant'", - "try", - format!("{left_expr}.checked_sub({right_expr}).unwrap()"), - applicability, - ); -} diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index 1666e8e5ae324..885649074ab63 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -101,7 +101,7 @@ fn upcast_comparison_bounds_err<'tcx>( invert: bool, ) { if let Some((lb, ub)) = lhs_bounds - && let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs) + && let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs, span.ctxt()) { if rel == Rel::Eq || rel == Rel::Ne { if norm_rhs_val < lb || norm_rhs_val > ub { diff --git a/clippy_lints/src/item_name_repetitions.rs b/clippy_lints/src/item_name_repetitions.rs index 945bb84708f85..76f5fdfaa8dcf 100644 --- a/clippy_lints/src/item_name_repetitions.rs +++ b/clippy_lints/src/item_name_repetitions.rs @@ -1,11 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir}; -use clippy_utils::is_bool; -use clippy_utils::macros::span_is_local; -use clippy_utils::source::is_present_in_source; use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case}; +use clippy_utils::{is_bool, is_from_proc_macro}; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, QPath, TyKind, Variant, VariantData}; +use rustc_hir::{Body, EnumDef, FieldDef, Item, ItemKind, QPath, TyKind, UseKind, Variant, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::symbol::Symbol; @@ -158,7 +156,8 @@ declare_clippy_lint! { } pub struct ItemNameRepetitions { - modules: Vec<(Symbol, String, OwnerId)>, + /// The module path the lint pass is in. + modules: Vec, enum_threshold: u64, struct_threshold: u64, avoid_breaking_exported_api: bool, @@ -167,6 +166,17 @@ pub struct ItemNameRepetitions { allowed_prefixes: FxHashSet, } +struct ModInfo { + name: Symbol, + name_camel: String, + /// Does this module have the `pub` visibility modifier. + is_public: bool, + /// How many bodies are between this module and the current lint pass position. + /// + /// Only the most recently seen module is updated when entering/exiting a body. + in_body_count: u32, +} + impl ItemNameRepetitions { pub fn new(conf: &'static Conf) -> Self { Self { @@ -458,71 +468,109 @@ fn check_enum_tuple_path_match(variant_name: &str, variant_data: VariantData<'_> } impl LateLintPass<'_> for ItemNameRepetitions { - fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) { - let Some(_ident) = item.kind.ident() else { return }; - - let last = self.modules.pop(); - assert!(last.is_some()); + fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) { + if matches!(item.kind, ItemKind::Mod(..)) { + let prev = self.modules.pop(); + debug_assert!(prev.is_some()); + } } - fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - let Some(ident) = item.kind.ident() else { return }; - - let item_name = ident.name.as_str(); - let item_camel = to_camel_case(item_name); - if !item.span.from_expansion() && is_present_in_source(cx, item.span) - && let [.., (mod_name, mod_camel, mod_owner_id)] = &*self.modules - // constants don't have surrounding modules - && !mod_camel.is_empty() - { - if mod_name == &ident.name - && let ItemKind::Mod(..) = item.kind - && (!self.allow_private_module_inception || cx.tcx.visibility(mod_owner_id.def_id).is_public()) - { - span_lint( - cx, - MODULE_INCEPTION, - item.span, - "module has the same name as its containing module", - ); - } + fn check_body(&mut self, _: &LateContext<'_>, _: &Body<'_>) { + if let [.., last] = &mut *self.modules { + last.in_body_count += 1; + } + } - // The `module_name_repetitions` lint should only trigger if the item has the module in its - // name. Having the same name is only accepted if `allow_exact_repetition` is set to `true`. + fn check_body_post(&mut self, _: &LateContext<'_>, _: &Body<'_>) { + if let [.., last] = &mut *self.modules { + last.in_body_count -= 1; + } + } - let both_are_public = - cx.tcx.visibility(item.owner_id).is_public() && cx.tcx.visibility(mod_owner_id.def_id).is_public(); + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + let ident = match item.kind { + ItemKind::Mod(ident, _) => { + if let [.., prev] = &*self.modules + && prev.name == ident.name + && prev.in_body_count == 0 + && (!self.allow_private_module_inception || prev.is_public) + && !item.span.from_expansion() + && !is_from_proc_macro(cx, item) + { + span_lint( + cx, + MODULE_INCEPTION, + item.span, + "module has the same name as its containing module", + ); + } + ident + }, - if both_are_public && !self.allow_exact_repetitions && item_camel == *mod_camel { - span_lint( - cx, - MODULE_NAME_REPETITIONS, - ident.span, - "item name is the same as its containing module's name", - ); - } + ItemKind::Enum(ident, _, def) => { + if !ident.span.in_external_macro(cx.tcx.sess.source_map()) { + self.check_variants(cx, item, &def); + } + ident + }, + ItemKind::Struct(ident, _, data) => { + if let VariantData::Struct { fields, .. } = data + && !ident.span.in_external_macro(cx.tcx.sess.source_map()) + { + self.check_fields(cx, item, fields); + } + ident + }, - let is_macro = matches!(item.kind, ItemKind::Macro(_, _, _)); - if both_are_public && item_camel.len() > mod_camel.len() && !is_macro { - let matching = count_match_start(mod_camel, &item_camel); - let rmatching = count_match_end(mod_camel, &item_camel); - let nchars = mod_camel.chars().count(); + ItemKind::Const(ident, ..) + | ItemKind::ExternCrate(_, ident) + | ItemKind::Fn { ident, .. } + | ItemKind::Macro(ident, ..) + | ItemKind::Static(_, ident, ..) + | ItemKind::Trait(_, _, _, ident, ..) + | ItemKind::TraitAlias(ident, ..) + | ItemKind::TyAlias(ident, ..) + | ItemKind::Union(ident, ..) + | ItemKind::Use(_, UseKind::Single(ident)) => ident, + + ItemKind::ForeignMod { .. } | ItemKind::GlobalAsm { .. } | ItemKind::Impl(_) | ItemKind::Use(..) => return, + }; - let is_word_beginning = |c: char| c == '_' || c.is_uppercase() || c.is_numeric(); + let item_name = ident.name.as_str(); + let item_camel = to_camel_case(item_name); - if matching.char_count == nchars { - match item_camel.chars().nth(nchars) { - Some(c) if is_word_beginning(c) => span_lint( + if let [.., prev] = &*self.modules + && prev.is_public + && prev.in_body_count == 0 + && !item.span.from_expansion() + && !matches!(item.kind, ItemKind::Macro(..)) + && cx.tcx.visibility(item.owner_id).is_public() + { + if !self.allow_exact_repetitions && item_camel == prev.name_camel { + if !is_from_proc_macro(cx, item) { + span_lint( + cx, + MODULE_NAME_REPETITIONS, + ident.span, + "item name is the same as its containing module's name", + ); + } + } else if item_camel.len() > prev.name_camel.len() { + if let Some(s) = item_camel.strip_prefix(&prev.name_camel) + && let Some(c) = s.chars().next() + && (c == '_' || c.is_uppercase() || c.is_numeric()) + { + if !is_from_proc_macro(cx, item) { + span_lint( cx, MODULE_NAME_REPETITIONS, ident.span, "item name starts with its containing module's name", - ), - _ => (), + ); } - } - if rmatching.char_count == nchars - && !self.is_allowed_prefix(&item_camel[..item_camel.len() - rmatching.byte_count]) + } else if let Some(s) = item_camel.strip_suffix(&prev.name_camel) + && !self.is_allowed_prefix(s) + && !is_from_proc_macro(cx, item) { span_lint( cx, @@ -534,17 +582,13 @@ impl LateLintPass<'_> for ItemNameRepetitions { } } - if span_is_local(item.span) { - match item.kind { - ItemKind::Enum(_, _, def) => { - self.check_variants(cx, item, &def); - }, - ItemKind::Struct(_, _, VariantData::Struct { fields, .. }) => { - self.check_fields(cx, item, fields); - }, - _ => (), - } + if matches!(item.kind, ItemKind::Mod(..)) { + self.modules.push(ModInfo { + name: ident.name, + name_camel: item_camel, + is_public: cx.tcx.visibility(item.owner_id).is_public(), + in_body_count: 0, + }); } - self.modules.push((ident.name, item_camel, item.owner_id)); } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b0083b99f175f..815411348aa66 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -99,7 +99,6 @@ mod cognitive_complexity; mod collapsible_if; mod collection_is_never_read; mod comparison_chain; -mod copies; mod copy_iterator; mod crate_in_macro_def; mod create_dir; @@ -157,6 +156,7 @@ mod future_not_send; mod if_let_mutex; mod if_not_else; mod if_then_some_else_none; +mod ifs; mod ignored_unit_patterns; mod impl_hash_with_borrow_str_and_bytes; mod implicit_hasher; @@ -175,7 +175,6 @@ mod inherent_impl; mod inherent_to_string; 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; @@ -252,7 +251,6 @@ mod multiple_bound_locations; mod multiple_unsafe_ops_per_block; mod mut_key; mod mut_mut; -mod mut_reference; mod mutable_debug_assertion; mod mutex_atomic; mod needless_arbitrary_self_type; @@ -356,6 +354,7 @@ mod swap_ptr_to_ref; mod tabs_in_doc_comments; mod temporary_assignment; mod tests_outside_test_module; +mod time_subtraction; mod to_digit_is_some; mod to_string_trait_impl; mod toplevel_ref_arg; @@ -374,6 +373,7 @@ mod unit_types; mod unnecessary_box_returns; mod unnecessary_literal_bound; mod unnecessary_map_on_constructor; +mod unnecessary_mut_passed; mod unnecessary_owned_empty_strings; mod unnecessary_self_imports; mod unnecessary_semicolon; @@ -481,8 +481,8 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach)); store.register_late_pass(|_| Box::new(misc::LintPass)); store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction)); - store.register_late_pass(|_| Box::new(mut_mut::MutMut)); - store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed)); + store.register_late_pass(|_| Box::new(mut_mut::MutMut::default())); + store.register_late_pass(|_| Box::new(unnecessary_mut_passed::UnnecessaryMutPassed)); store.register_late_pass(|_| Box::>::default()); store.register_late_pass(|_| Box::new(len_zero::LenZero)); store.register_late_pass(move |_| Box::new(attrs::Attributes::new(conf))); @@ -548,7 +548,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum)); store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons)); store.register_late_pass(|_| Box::::default()); - store.register_late_pass(move |tcx| Box::new(copies::CopyAndPaste::new(tcx, conf))); + store.register_late_pass(move |tcx| Box::new(ifs::CopyAndPaste::new(tcx, conf))); store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator)); let format_args = format_args_storage.clone(); store.register_late_pass(move |_| Box::new(format::UselessFormat::new(format_args.clone()))); @@ -588,13 +588,13 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(map_unit_fn::MapUnit)); store.register_late_pass(|_| Box::new(inherent_impl::MultipleInherentImpl)); store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)); - store.register_late_pass(|_| Box::new(unwrap::Unwrap)); + store.register_late_pass(move |_| Box::new(unwrap::Unwrap::new(conf))); store.register_late_pass(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf))); store.register_late_pass(move |tcx| Box::new(non_copy_const::NonCopyConst::new(tcx, conf))); store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit)); store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(conf))); - store.register_late_pass(|_| Box::new(assertions_on_constants::AssertionsOnConstants)); + store.register_late_pass(|_| Box::new(assertions_on_constants::AssertionsOnConstants::new(conf))); store.register_late_pass(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates)); store.register_late_pass(|_| Box::new(inherent_to_string::InherentToString)); store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(conf))); @@ -670,7 +670,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10)); store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(conf))); store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison)); - store.register_early_pass(move || Box::new(module_style::ModStyle)); + store.register_early_pass(move || Box::new(module_style::ModStyle::default())); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |tcx| Box::new(disallowed_types::DisallowedTypes::new(tcx, conf))); store.register_late_pass(move |tcx| Box::new(missing_enforced_import_rename::ImportRename::new(tcx, conf))); @@ -717,7 +717,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(move |_| Box::new(manual_rotate::ManualRotate)); store.register_late_pass(move |_| Box::new(operators::Operators::new(conf))); store.register_late_pass(move |_| Box::new(std_instead_of_core::StdReexports::new(conf))); - store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(conf))); + store.register_late_pass(move |_| Box::new(time_subtraction::UncheckedTimeSubtraction::new(conf))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); store.register_late_pass(move |_| Box::new(manual_abs_diff::ManualAbsDiff::new(conf))); store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(conf))); diff --git a/clippy_lints/src/macro_metavars_in_unsafe.rs b/clippy_lints/src/macro_metavars_in_unsafe.rs index c5acaf0999332..a323c7cf8307c 100644 --- a/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -2,11 +2,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::is_lint_allowed; use itertools::Itertools; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; -use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource}; -use rustc_hir::attrs::AttributeKind; -use rustc_hir::find_attr; +use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource, find_attr}; use rustc_lint::{LateContext, LateLintPass, Level, LintContext}; use rustc_middle::lint::LevelAndSource; use rustc_session::impl_lint_pass; @@ -148,8 +147,8 @@ struct BodyVisitor<'a, 'tcx> { } fn is_public_macro(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { - ( cx.effective_visibilities.is_exported(def_id) || - find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::MacroExport{..}) ) + (cx.effective_visibilities.is_exported(def_id) + || find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::MacroExport { .. })) && !cx.tcx.is_doc_hidden(def_id) } diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs index bd2785fea2709..60782f445ab91 100644 --- a/clippy_lints/src/manual_float_methods.rs +++ b/clippy_lints/src/manual_float_methods.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_utils::consts::{ConstEvalCtxt, Constant}; +use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; @@ -146,13 +146,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { ) && let [first, second, const_1, const_2] = exprs && let ecx = ConstEvalCtxt::new(cx) - && let Some(const_1) = ecx.eval(const_1) - && let Some(const_2) = ecx.eval(const_2) + && let ctxt = expr.span.ctxt() + && let Some(const_1) = ecx.eval_local(const_1, ctxt) + && let Some(const_2) = ecx.eval_local(const_2, ctxt) && path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s)) // The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in // case somebody does that for some reason - && (is_infinity(&const_1) && is_neg_infinity(&const_2) - || is_neg_infinity(&const_1) && is_infinity(&const_2)) + && (const_1.is_pos_infinity() && const_2.is_neg_infinity() + || const_1.is_neg_infinity() && const_2.is_pos_infinity()) && let Some(local_snippet) = first.span.get_source_text(cx) { let variant = match (kind.node, lhs_kind.node, rhs_kind.node) { @@ -201,21 +202,3 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { } } } - -fn is_infinity(constant: &Constant<'_>) -> bool { - match constant { - // FIXME(f16_f128): add f16 and f128 when constants are available - Constant::F32(float) => *float == f32::INFINITY, - Constant::F64(float) => *float == f64::INFINITY, - _ => false, - } -} - -fn is_neg_infinity(constant: &Constant<'_>) -> bool { - match constant { - // FIXME(f16_f128): add f16 and f128 when constants are available - Constant::F32(float) => *float == f32::NEG_INFINITY, - Constant::F64(float) => *float == f64::NEG_INFINITY, - _ => false, - } -} diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 41e07e26bff0a..1e91a429fe45c 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -8,6 +8,7 @@ use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; +use rustc_span::SyntaxContext; declare_clippy_lint! { /// ### What it does @@ -58,13 +59,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { && add_lhs.span.ctxt() == ctxt && add_rhs.span.ctxt() == ctxt && !expr.span.in_external_macro(cx.sess().source_map()) - && let Some(const1) = check_for_unsigned_int_constant(cx, rem_rhs) - && let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, add_lhs, add_rhs) + && let Some(const1) = check_for_unsigned_int_constant(cx, ctxt, rem_rhs) + && let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, ctxt, add_lhs, add_rhs) && let ExprKind::Binary(rem2_op, rem2_lhs, rem2_rhs) = add_other.kind && rem2_op.node == BinOpKind::Rem && const1 == const2 && let Some(hir_id) = path_to_local(rem2_lhs) - && let Some(const3) = check_for_unsigned_int_constant(cx, rem2_rhs) + && let Some(const3) = check_for_unsigned_int_constant(cx, ctxt, rem2_rhs) // Also ensures the const is nonzero since zero can't be a divisor && const2 == const3 && rem2_lhs.span.ctxt() == ctxt @@ -103,16 +104,21 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { // constant along with the other expression unchanged if so fn check_for_either_unsigned_int_constant<'a>( cx: &'a LateContext<'_>, + ctxt: SyntaxContext, left: &'a Expr<'_>, right: &'a Expr<'_>, ) -> Option<(u128, &'a Expr<'a>)> { - check_for_unsigned_int_constant(cx, left) + check_for_unsigned_int_constant(cx, ctxt, left) .map(|int_const| (int_const, right)) - .or_else(|| check_for_unsigned_int_constant(cx, right).map(|int_const| (int_const, left))) + .or_else(|| check_for_unsigned_int_constant(cx, ctxt, right).map(|int_const| (int_const, left))) } -fn check_for_unsigned_int_constant<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option { - let int_const = ConstEvalCtxt::new(cx).eval_full_int(expr)?; +fn check_for_unsigned_int_constant<'a>( + cx: &'a LateContext<'_>, + ctxt: SyntaxContext, + expr: &'a Expr<'_>, +) -> Option { + let int_const = ConstEvalCtxt::new(cx).eval_full_int(expr, ctxt)?; match int_const { FullInt::S(s) => s.try_into().ok(), FullInt::U(u) => Some(u), diff --git a/clippy_lints/src/manual_rotate.rs b/clippy_lints/src/manual_rotate.rs index 06ee00c2cef3c..22e3407303f07 100644 --- a/clippy_lints/src/manual_rotate.rs +++ b/clippy_lints/src/manual_rotate.rs @@ -66,7 +66,7 @@ fn parse_shift<'tcx>( BinOpKind::Shr => ShiftDirection::Right, _ => return None, }; - let const_expr = ConstEvalCtxt::new(cx).eval(r)?; + let const_expr = ConstEvalCtxt::new(cx).eval_local(r, expr.span.ctxt())?; if let Constant::Int(shift) = const_expr { return Some((dir, shift, l)); } diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 07cce4046ca4f..f5d15310879a3 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -16,7 +16,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext as _}; use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::{Symbol, sym}; +use rustc_span::{Symbol, SyntaxContext, sym}; use std::iter; declare_clippy_lint! { @@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { return; } - let (strippings, bindings) = find_stripping(cx, strip_kind, target_res, pattern, then); + let (strippings, bindings) = find_stripping(cx, strip_kind, target_res, pattern, then, expr.span.ctxt()); if !strippings.is_empty() && self.msrv.meets(cx, msrvs::STR_STRIP_PREFIX) { let kind_word = match strip_kind { StripKind::Prefix => "prefix", @@ -166,8 +166,8 @@ fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E } // Returns the length of the `expr` if it's a constant string or char. -fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - let value = ConstEvalCtxt::new(cx).eval(expr)?; +fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> Option { + let value = ConstEvalCtxt::new(cx).eval_local(expr, ctxt)?; match value { Constant::Str(value) => Some(value.len() as u128), Constant::Char(value) => Some(value.len_utf8() as u128), @@ -176,13 +176,18 @@ fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } // Tests if `expr` equals the length of the pattern. -fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'tcx Expr<'_>) -> bool { +fn eq_pattern_length<'tcx>( + cx: &LateContext<'tcx>, + pattern: &Expr<'_>, + expr: &'tcx Expr<'_>, + ctxt: SyntaxContext, +) -> bool { if let ExprKind::Lit(Spanned { node: LitKind::Int(n, _), .. }) = expr.kind { - constant_length(cx, pattern).is_some_and(|length| n == length) + constant_length(cx, pattern, ctxt).is_some_and(|length| n == length) } else { len_arg(cx, expr).is_some_and(|arg| eq_expr_value(cx, pattern, arg)) } @@ -215,6 +220,7 @@ fn find_stripping<'tcx>( target: Res, pattern: &'tcx Expr<'_>, expr: &'tcx Expr<'tcx>, + ctxt: SyntaxContext, ) -> (Vec<&'tcx Expr<'tcx>>, FxHashMap) { struct StrippingFinder<'a, 'tcx> { cx: &'a LateContext<'tcx>, @@ -223,6 +229,7 @@ fn find_stripping<'tcx>( pattern: &'tcx Expr<'tcx>, results: Vec<&'tcx Expr<'tcx>>, bindings: FxHashMap, + ctxt: SyntaxContext, } impl<'tcx> Visitor<'tcx> for StrippingFinder<'_, 'tcx> { @@ -236,7 +243,7 @@ fn find_stripping<'tcx>( { match (self.strip_kind, start, end) { (StripKind::Prefix, Some(start), None) => { - if eq_pattern_length(self.cx, self.pattern, start) { + if eq_pattern_length(self.cx, self.pattern, start, self.ctxt) { self.results.push(ex); return; } @@ -252,7 +259,7 @@ fn find_stripping<'tcx>( && let Some(left_arg) = len_arg(self.cx, left) && let ExprKind::Path(left_path) = &left_arg.kind && self.cx.qpath_res(left_path, left_arg.hir_id) == self.target - && eq_pattern_length(self.cx, self.pattern, right) + && eq_pattern_length(self.cx, self.pattern, right, self.ctxt) { self.results.push(ex); return; @@ -280,6 +287,7 @@ fn find_stripping<'tcx>( pattern, results: vec![], bindings: FxHashMap::default(), + ctxt, }; walk_expr(&mut finder, expr); (finder.results, finder.bindings) diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index 8c3f52542d917..ac9e51890362b 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -155,7 +155,7 @@ fn handle( && cx.typeck_results().expr_adjustments(body_some).is_empty() && let Some(or_body_snippet) = peel_blocks(body_none).span.get_source_text(cx) && let Some(indent) = indent_of(cx, expr.span) - && ConstEvalCtxt::new(cx).eval_simple(body_none).is_some() + && ConstEvalCtxt::new(cx).eval_local(body_none, expr.span.ctxt()).is_some() { let reindented_or_body = reindent_multiline(&or_body_snippet, true, Some(indent)); let mut app = Applicability::MachineApplicable; diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index ae277da089fd2..818e504245549 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{SpanlessEq, SpanlessHash, fulfill_or_allowed, is_lint_allowed, path_to_local, search_same}; +use clippy_utils::{SpanlessEq, fulfill_or_allowed, hash_expr, is_lint_allowed, path_to_local, search_same}; use core::cmp::Ordering; use core::{iter, slice}; use itertools::Itertools; @@ -18,11 +18,7 @@ use super::MATCH_SAME_ARMS; #[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { - let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 { - let mut h = SpanlessHash::new(cx); - h.hash_expr(arm.body); - h.finish() - }; + let hash = |&(_, arm): &(_, &Arm<'_>)| hash_expr(cx, arm.body); let arena = DroplessArena::default(); let normalized_pats: Vec<_> = arms @@ -35,9 +31,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { .iter() .enumerate() .map(|(i, pat)| { - normalized_pats[i + 1..] - .iter() - .enumerate() + (normalized_pats[i + 1..].iter().enumerate()) .find_map(|(j, other)| pat.has_overlapping_values(other).then_some(i + 1 + j)) .unwrap_or(normalized_pats.len()) }) @@ -48,16 +42,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { .iter() .enumerate() .map(|(i, pat)| { - normalized_pats[..i] - .iter() - .enumerate() - .rev() - .zip(forwards_blocking_idxs[..i].iter().copied().rev()) - .skip_while(|&(_, forward_block)| forward_block > i) - .find_map(|((j, other), forward_block)| { - (forward_block == i || pat.has_overlapping_values(other)).then_some(j) - }) - .unwrap_or(0) + iter::zip( + normalized_pats[..i].iter().enumerate().rev(), + forwards_blocking_idxs[..i].iter().copied().rev(), + ) + .skip_while(|&(_, forward_block)| forward_block > i) + .find_map(|((j, other), forward_block)| { + (forward_block == i || pat.has_overlapping_values(other)).then_some(j) + }) + .unwrap_or(0) }) .collect(); @@ -158,12 +151,12 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { .map(|(_, arm)| arm.pat.span.get_source_text(cx)) .collect::>>() { - let mut suggs = src + let suggs = src .iter() .map(|(_, arm)| (adjusted_arm_span(cx, arm.span), String::new())) + .chain([(dest.pat.span, pat_snippets.iter().join(" | "))]) .collect_vec(); - suggs.push((dest.pat.span, pat_snippets.iter().join(" | "))); diag.multipart_suggestion_verbose( "otherwise merge the patterns into a single arm", suggs, @@ -396,10 +389,7 @@ impl<'a> NormalizedPat<'a> { if lpath != rpath { return false; } - lpats - .iter() - .zip(rpats.iter()) - .all(|(lpat, rpat)| lpat.has_overlapping_values(rpat)) + iter::zip(lpats, rpats).all(|(lpat, rpat)| lpat.has_overlapping_values(rpat)) }, (Self::Path(x), Self::Path(y)) => x == y, (Self::LitStr(x), Self::LitStr(y)) => x == y, @@ -409,7 +399,7 @@ impl<'a> NormalizedPat<'a> { (Self::Range(ref x), Self::Range(ref y)) => x.overlaps(y), (Self::Range(ref range), Self::LitInt(x)) | (Self::LitInt(x), Self::Range(ref range)) => range.contains(x), (Self::Slice(lpats, None), Self::Slice(rpats, None)) => { - lpats.len() == rpats.len() && lpats.iter().zip(rpats.iter()).all(|(x, y)| x.has_overlapping_values(y)) + lpats.len() == rpats.len() && iter::zip(lpats, rpats).all(|(x, y)| x.has_overlapping_values(y)) }, (Self::Slice(pats, None), Self::Slice(front, Some(back))) | (Self::Slice(front, Some(back)), Self::Slice(pats, None)) => { @@ -418,16 +408,12 @@ impl<'a> NormalizedPat<'a> { if pats.len() < front.len() + back.len() { return false; } - pats[..front.len()] - .iter() - .zip(front.iter()) - .chain(pats[pats.len() - back.len()..].iter().zip(back.iter())) + iter::zip(&pats[..front.len()], front) + .chain(iter::zip(&pats[pats.len() - back.len()..], back)) .all(|(x, y)| x.has_overlapping_values(y)) }, - (Self::Slice(lfront, Some(lback)), Self::Slice(rfront, Some(rback))) => lfront - .iter() - .zip(rfront.iter()) - .chain(lback.iter().rev().zip(rback.iter().rev())) + (Self::Slice(lfront, Some(lback)), Self::Slice(rfront, Some(rback))) => iter::zip(lfront, rfront) + .chain(iter::zip(lback.iter().rev(), rback.iter().rev())) .all(|(x, y)| x.has_overlapping_values(y)), // Enums can mix unit variants with tuple/struct variants. These can never overlap. diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index d3136c89178e6..d76218e6305b0 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{ConstEvalCtxt, FullInt, mir_to_const}; +use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt}; use clippy_utils::diagnostics::span_lint_and_note; use core::cmp::Ordering; use rustc_hir::{Arm, Expr, PatKind, RangeEnd}; @@ -35,12 +35,12 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) let lhs_const = if let Some(lhs) = lhs { ConstEvalCtxt::new(cx).eval_pat_expr(lhs)? } else { - mir_to_const(cx.tcx, ty.numeric_min_val(cx.tcx)?)? + Constant::new_numeric_min(cx.tcx, ty)? }; let rhs_const = if let Some(rhs) = rhs { ConstEvalCtxt::new(cx).eval_pat_expr(rhs)? } else { - mir_to_const(cx.tcx, ty.numeric_max_val(cx.tcx)?)? + Constant::new_numeric_max(cx.tcx, ty)? }; let lhs_val = lhs_const.int_value(cx.tcx, ty)?; let rhs_val = rhs_const.int_value(cx.tcx, ty)?; diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index c936c96f9719a..9d0115791838c 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -269,66 +269,61 @@ fn find_method_sugg_for_if_let<'tcx>( } pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) { - if arms.len() == 2 { - let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); - - if let Some((good_method, maybe_guard)) = found_good_method(cx, arms, node_pair) { - let span = is_expn_of(expr.span, sym::matches).unwrap_or(expr.span.to(op.span)); - let result_expr = match &op.kind { - ExprKind::AddrOf(_, _, borrowed) => borrowed, - _ => op, - }; - let mut app = Applicability::MachineApplicable; - let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_paren(); - let mut sugg = format!("{receiver_sugg}.{good_method}"); - - if let Some(guard) = maybe_guard { - // wow, the HIR for match guards in `PAT if let PAT = expr && expr => ...` is annoying! - // `guard` here is `Guard::If` with the let expression somewhere deep in the tree of exprs, - // counter to the intuition that it should be `Guard::IfLet`, so we need another check - // to see that there aren't any let chains anywhere in the guard, as that would break - // if we suggest `t.is_none() && (let X = y && z)` for: - // `match t { None if let X = y && z => true, _ => false }` - let has_nested_let_chain = for_each_expr_without_closures(guard, |expr| { - if matches!(expr.kind, ExprKind::Let(..)) { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - }) - .is_some(); - - if has_nested_let_chain { - return; + if let Ok(arms) = arms.try_into() // TODO: use `slice::as_array` once stabilized + && let Some((good_method, maybe_guard)) = found_good_method(cx, arms) + { + let span = is_expn_of(expr.span, sym::matches).unwrap_or(expr.span.to(op.span)); + let result_expr = match &op.kind { + ExprKind::AddrOf(_, _, borrowed) => borrowed, + _ => op, + }; + let mut app = Applicability::MachineApplicable; + let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_paren(); + let mut sugg = format!("{receiver_sugg}.{good_method}"); + + if let Some(guard) = maybe_guard { + // wow, the HIR for match guards in `PAT if let PAT = expr && expr => ...` is annoying! + // `guard` here is `Guard::If` with the let expression somewhere deep in the tree of exprs, + // counter to the intuition that it should be `Guard::IfLet`, so we need another check + // to see that there aren't any let chains anywhere in the guard, as that would break + // if we suggest `t.is_none() && (let X = y && z)` for: + // `match t { None if let X = y && z => true, _ => false }` + let has_nested_let_chain = for_each_expr_without_closures(guard, |expr| { + if matches!(expr.kind, ExprKind::Let(..)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } + }) + .is_some(); - let guard = Sugg::hir(cx, guard, ".."); - let _ = write!(sugg, " && {}", guard.maybe_paren()); + if has_nested_let_chain { + return; } - span_lint_and_sugg( - cx, - REDUNDANT_PATTERN_MATCHING, - span, - format!("redundant pattern matching, consider using `{good_method}`"), - "try", - sugg, - app, - ); + let guard = Sugg::hir(cx, guard, ".."); + let _ = write!(sugg, " && {}", guard.maybe_paren()); } + + span_lint_and_sugg( + cx, + REDUNDANT_PATTERN_MATCHING, + span, + format!("redundant pattern matching, consider using `{good_method}`"), + "try", + sugg, + app, + ); } } fn found_good_method<'tcx>( cx: &LateContext<'_>, - arms: &'tcx [Arm<'tcx>], - node: (&PatKind<'_>, &PatKind<'_>), + arms: &'tcx [Arm<'tcx>; 2], ) -> Option<(&'static str, Option<&'tcx Expr<'tcx>>)> { - match node { - (PatKind::TupleStruct(path_left, patterns_left, _), PatKind::TupleStruct(path_right, patterns_right, _)) - if patterns_left.len() == 1 && patterns_right.len() == 1 => - { - if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) { + match (&arms[0].pat.kind, &arms[1].pat.kind) { + (PatKind::TupleStruct(path_left, [pattern_left], _), PatKind::TupleStruct(path_right, [pattern_right], _)) => { + if let (PatKind::Wild, PatKind::Wild) = (&pattern_left.kind, &pattern_right.kind) { find_good_method_for_match( cx, arms, @@ -356,7 +351,7 @@ fn found_good_method<'tcx>( } }, ( - PatKind::TupleStruct(path_left, patterns, _), + PatKind::TupleStruct(path_left, [pattern], _), PatKind::Expr(PatExpr { kind: PatExprKind::Path(path_right), .. @@ -367,9 +362,9 @@ fn found_good_method<'tcx>( kind: PatExprKind::Path(path_left), .. }), - PatKind::TupleStruct(path_right, patterns, _), - ) if patterns.len() == 1 => { - if let PatKind::Wild = patterns[0].kind { + PatKind::TupleStruct(path_right, [pattern], _), + ) => { + if let PatKind::Wild = pattern.kind { find_good_method_for_match( cx, arms, @@ -396,8 +391,8 @@ fn found_good_method<'tcx>( None } }, - (PatKind::TupleStruct(path_left, patterns, _), PatKind::Wild) if patterns.len() == 1 => { - if let PatKind::Wild = patterns[0].kind { + (PatKind::TupleStruct(path_left, [pattern], _), PatKind::Wild) => { + if let PatKind::Wild = pattern.kind { get_good_method(cx, arms, path_left) } else { None @@ -426,31 +421,23 @@ fn get_ident(path: &QPath<'_>) -> Option { fn get_good_method<'tcx>( cx: &LateContext<'_>, - arms: &'tcx [Arm<'tcx>], + arms: &'tcx [Arm<'tcx>; 2], path_left: &QPath<'_>, ) -> Option<(&'static str, Option<&'tcx Expr<'tcx>>)> { - if let Some(name) = get_ident(path_left) { - let (expected_item_left, should_be_left, should_be_right) = match name.as_str() { - "Ok" => (Item::Lang(ResultOk), "is_ok()", "is_err()"), - "Err" => (Item::Lang(ResultErr), "is_err()", "is_ok()"), - "Some" => (Item::Lang(OptionSome), "is_some()", "is_none()"), - "None" => (Item::Lang(OptionNone), "is_none()", "is_some()"), - "Ready" => (Item::Lang(PollReady), "is_ready()", "is_pending()"), - "Pending" => (Item::Lang(PollPending), "is_pending()", "is_ready()"), - "V4" => (Item::Diag(sym::IpAddr, sym::V4), "is_ipv4()", "is_ipv6()"), - "V6" => (Item::Diag(sym::IpAddr, sym::V6), "is_ipv6()", "is_ipv4()"), - _ => return None, - }; - return find_good_method_for_matches_macro( - cx, - arms, - path_left, - expected_item_left, - should_be_left, - should_be_right, - ); - } - None + let ident = get_ident(path_left)?; + + let (expected_item_left, should_be_left, should_be_right) = match ident.name { + sym::Ok => (Item::Lang(ResultOk), "is_ok()", "is_err()"), + sym::Err => (Item::Lang(ResultErr), "is_err()", "is_ok()"), + sym::Some => (Item::Lang(OptionSome), "is_some()", "is_none()"), + sym::None => (Item::Lang(OptionNone), "is_none()", "is_some()"), + sym::Ready => (Item::Lang(PollReady), "is_ready()", "is_pending()"), + sym::Pending => (Item::Lang(PollPending), "is_pending()", "is_ready()"), + sym::V4 => (Item::Diag(sym::IpAddr, sym::V4), "is_ipv4()", "is_ipv6()"), + sym::V6 => (Item::Diag(sym::IpAddr, sym::V6), "is_ipv6()", "is_ipv4()"), + _ => return None, + }; + find_good_method_for_matches_macro(cx, arms, path_left, expected_item_left, should_be_left, should_be_right) } #[derive(Clone, Copy)] @@ -490,7 +477,7 @@ fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expecte #[expect(clippy::too_many_arguments)] fn find_good_method_for_match<'a, 'tcx>( cx: &LateContext<'_>, - arms: &'tcx [Arm<'tcx>], + arms: &'tcx [Arm<'tcx>; 2], path_left: &QPath<'_>, path_right: &QPath<'_>, expected_item_left: Item, @@ -525,7 +512,7 @@ fn find_good_method_for_match<'a, 'tcx>( fn find_good_method_for_matches_macro<'a, 'tcx>( cx: &LateContext<'_>, - arms: &'tcx [Arm<'tcx>], + arms: &'tcx [Arm<'tcx>; 2], path_left: &QPath<'_>, expected_item_left: Item, should_be_left: &'a str, diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 83939d325794e..02f87512966ba 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -22,17 +22,16 @@ use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE}; /// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty /// match arms. fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool { - if let Some(ff) = span.get_source_range(cx) - && let Some(text) = ff.as_str() - { - text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*") - } else { - false - } + span.check_source_text(cx, |text| text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")) } -#[rustfmt::skip] -pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], expr: &'tcx Expr<'_>, contains_comments: bool) { +pub(crate) fn check<'tcx>( + cx: &LateContext<'tcx>, + ex: &'tcx Expr<'_>, + arms: &'tcx [Arm<'_>], + expr: &'tcx Expr<'_>, + contains_comments: bool, +) { if let [arm1, arm2] = arms && !arms.iter().any(|arm| arm.guard.is_some() || arm.pat.span.from_expansion()) && !expr.span.from_expansion() @@ -224,13 +223,13 @@ enum PatState<'a> { Wild, /// A std enum we know won't be extended. Tracks the states of each variant separately. /// - /// This is not used for `Option` since it uses the current pattern to track it's state. + /// This is not used for `Option` since it uses the current pattern to track its state. StdEnum(&'a mut [PatState<'a>]), /// Either the initial state for a pattern or a non-std enum. There is currently no need to /// distinguish these cases. /// /// For non-std enums there's no need to track the state of sub-patterns as the state of just - /// this pattern on it's own is enough for linting. Consider two cases: + /// this pattern on its own is enough for linting. Consider two cases: /// * This enum has no wild match. This case alone is enough to determine we can lint. /// * This enum has a wild match and therefore all sub-patterns also have a wild match. /// @@ -378,7 +377,11 @@ impl<'a> PatState<'a> { self.add_pat(cx, pat) }, PatKind::Tuple([sub_pat], pos) - if pos.as_opt_usize().is_none() || cx.typeck.pat_ty(pat).tuple_fields().len() == 1 => + // `pat` looks like `(sub_pat)`, without a `..` -- has only one sub-pattern + if pos.as_opt_usize().is_none() + // `pat` looks like `(sub_pat, ..)` or `(.., sub_pat)`, but its type is a unary tuple, + // so it still only has one sub-pattern + || cx.typeck.pat_ty(pat).tuple_fields().len() == 1 => { self.add_pat(cx, sub_pat) }, diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index e39916f733d59..95ecef5987007 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::{snippet, snippet_with_applicability}; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_non_aggregate_primitive_type; use clippy_utils::{ @@ -269,14 +269,11 @@ fn check_replace_with_default( ), |diag| { if !expr.span.from_expansion() { - let suggestion = format!("{top_crate}::mem::take({})", snippet(cx, dest.span, "")); + let mut applicability = Applicability::MachineApplicable; + let (dest_snip, _) = snippet_with_context(cx, dest.span, expr.span.ctxt(), "", &mut applicability); + let suggestion = format!("{top_crate}::mem::take({dest_snip})"); - diag.span_suggestion( - expr.span, - "consider using", - suggestion, - Applicability::MachineApplicable, - ); + diag.span_suggestion(expr.span, "consider using", suggestion, applicability); } }, ); diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index 72f83b245a0cb..e1a9a79e20eea 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -10,51 +10,68 @@ use rustc_span::sym; use super::FILTER_NEXT; -/// lint use of `filter().next()` for `Iterators` +#[derive(Copy, Clone)] +pub(super) enum Direction { + Forward, + Backward, +} + +/// lint use of `filter().next()` for `Iterator` and `filter().next_back()` for +/// `DoubleEndedIterator` pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, recv: &'tcx hir::Expr<'_>, filter_arg: &'tcx hir::Expr<'_>, + direction: Direction, ) { - // lint if caller of `.filter().next()` is an Iterator - let recv_impls_iterator = cx + // lint if caller of `.filter().next()` is an Iterator or `.filter().next_back()` is a + // DoubleEndedIterator + let (required_trait, next_method, find_method) = match direction { + Direction::Forward => (sym::Iterator, "next", "find"), + Direction::Backward => (sym::DoubleEndedIterator, "next_back", "rfind"), + }; + if !cx .tcx - .get_diagnostic_item(sym::Iterator) - .is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(recv), id, &[])); - if recv_impls_iterator { - let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ - `.find(..)` instead"; - let filter_snippet = snippet(cx, filter_arg.span, ".."); - if filter_snippet.lines().count() <= 1 { - let iter_snippet = snippet(cx, recv.span, ".."); - // add note if not multi-line - span_lint_and_then(cx, FILTER_NEXT, expr.span, msg, |diag| { - let (applicability, pat) = if let Some(id) = path_to_local_with_projections(recv) - && let hir::Node::Pat(pat) = cx.tcx.hir_node(id) - && let hir::PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind - { - (Applicability::Unspecified, Some((pat.span, ident))) - } else { - (Applicability::MachineApplicable, None) - }; + .get_diagnostic_item(required_trait) + .is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(recv), id, &[])) + { + return; + } + let msg = format!( + "called `filter(..).{next_method}()` on an `{}`. This is more succinctly expressed by calling \ + `.{find_method}(..)` instead", + required_trait.as_str() + ); + let filter_snippet = snippet(cx, filter_arg.span, ".."); + if filter_snippet.lines().count() <= 1 { + let iter_snippet = snippet(cx, recv.span, ".."); + // add note if not multi-line + span_lint_and_then(cx, FILTER_NEXT, expr.span, msg, |diag| { + let (applicability, pat) = if let Some(id) = path_to_local_with_projections(recv) + && let hir::Node::Pat(pat) = cx.tcx.hir_node(id) + && let hir::PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind + { + (Applicability::Unspecified, Some((pat.span, ident))) + } else { + (Applicability::MachineApplicable, None) + }; - diag.span_suggestion( - expr.span, - "try", - format!("{iter_snippet}.find({filter_snippet})"), - applicability, - ); + diag.span_suggestion( + expr.span, + "try", + format!("{iter_snippet}.{find_method}({filter_snippet})"), + applicability, + ); - if let Some((pat_span, ident)) = pat { - diag.span_help( - pat_span, - format!("you will also need to make `{ident}` mutable, because `find` takes `&mut self`"), - ); - } - }); - } else { - span_lint(cx, FILTER_NEXT, expr.span, msg); - } + if let Some((pat_span, ident)) = pat { + diag.span_help( + pat_span, + format!("you will also need to make `{ident}` mutable, because `{find_method}` takes `&mut self`"), + ); + } + }); + } else { + span_lint(cx, FILTER_NEXT, expr.span, msg); } } diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 47195fdd65f5f..ab21515f47f78 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs}; use rustc_errors::Applicability; @@ -16,6 +17,7 @@ pub fn check( method_name: Symbol, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>], + msrv: Msrv, ) { if args.is_empty() && method_name == sym::to_string @@ -26,6 +28,8 @@ pub fn check( && let self_ty = args.type_at(0) && let (deref_self_ty, deref_count, _) = peel_and_count_ty_refs(self_ty) && deref_count >= 1 + // Since Rust 1.82, the specialized `ToString` is properly called + && !msrv.meets(cx, msrvs::SPECIALIZED_TO_STRING_FOR_REFS) && specializes_tostring(cx, deref_self_ty) { span_lint_and_then( diff --git a/clippy_lints/src/methods/ip_constant.rs b/clippy_lints/src/methods/ip_constant.rs index a2ac4e54334ee..bf602811009a2 100644 --- a/clippy_lints/src/methods/ip_constant.rs +++ b/clippy_lints/src/methods/ip_constant.rs @@ -17,10 +17,12 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args cx.tcx.get_diagnostic_name(func_def_id), Some(sym::Ipv4Addr | sym::Ipv6Addr) ) + && let ecx = ConstEvalCtxt::new(cx) + && let ctxt = expr.span.ctxt() && let Some(args) = args .iter() .map(|arg| { - if let Some(Constant::Int(constant @ (0 | 1 | 127 | 255))) = ConstEvalCtxt::new(cx).eval(arg) { + if let Some(Constant::Int(constant @ (0 | 1 | 127 | 255))) = ecx.eval_local(arg, ctxt) { u8::try_from(constant).ok() } else { None diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs index 9c32e9ac539d9..2aeb6f42d05cd 100644 --- a/clippy_lints/src/methods/is_digit_ascii_radix.rs +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -18,7 +18,7 @@ pub(super) fn check<'tcx>( return; } - if let Some(radix_val) = ConstEvalCtxt::new(cx).eval_full_int(radix) { + if let Some(radix_val) = ConstEvalCtxt::new(cx).eval_full_int(radix, expr.span.ctxt()) { let (num, replacement) = match radix_val { FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"), FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"), diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs index 4bdf589f48762..0f8abd0172423 100644 --- a/clippy_lints/src/methods/iter_nth_zero.rs +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr if let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) && let def_id = item.owner_id.to_def_id() && is_trait_method(cx, expr, sym::Iterator) - && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) + && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval_local(arg, expr.span.ctxt()) && !is_lang_item_or_ctor(cx, def_id, LangItem::IteratorNext) { let mut app = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/iter_skip_zero.rs b/clippy_lints/src/methods/iter_skip_zero.rs index 39e440e784f6d..663e34437a307 100644 --- a/clippy_lints/src/methods/iter_skip_zero.rs +++ b/clippy_lints/src/methods/iter_skip_zero.rs @@ -11,13 +11,15 @@ use super::ITER_SKIP_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg_expr: &Expr<'_>) { if !expr.span.from_expansion() && is_trait_method(cx, expr, sym::Iterator) - && let Some(arg) = ConstEvalCtxt::new(cx).eval(arg_expr).and_then(|constant| { - if let Constant::Int(arg) = constant { - Some(arg) - } else { - None - } - }) + && let Some(arg) = ConstEvalCtxt::new(cx) + .eval_local(arg_expr, expr.span.ctxt()) + .and_then(|constant| { + if let Constant::Int(arg) = constant { + Some(arg) + } else { + None + } + }) && arg == 0 && !is_from_proc_macro(cx, expr) { diff --git a/clippy_lints/src/methods/lib.rs b/clippy_lints/src/methods/lib.rs new file mode 100644 index 0000000000000..84038283bcf8f --- /dev/null +++ b/clippy_lints/src/methods/lib.rs @@ -0,0 +1,70 @@ +use clippy_utils::sym; +use clippy_utils::ty::{implements_trait, is_copy}; +use rustc_hir::Mutability; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub(super) enum SelfKind { + Value, + Ref, + RefMut, + No, // When we want the first argument type to be different than `Self` +} + +impl SelfKind { + pub(super) fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + if ty == parent_ty { + true + } else if let Some(boxed_ty) = ty.boxed_ty() { + boxed_ty == parent_ty + } else if let ty::Adt(adt_def, args) = ty.kind() + && matches!(cx.tcx.get_diagnostic_name(adt_def.did()), Some(sym::Rc | sym::Arc)) + { + args.types().next() == Some(parent_ty) + } else { + false + } + } + + fn matches_ref<'a>(cx: &LateContext<'a>, mutability: Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + if let ty::Ref(_, t, m) = *ty.kind() { + return m == mutability && t == parent_ty; + } + + let trait_sym = match mutability { + Mutability::Not => sym::AsRef, + Mutability::Mut => sym::AsMut, + }; + + let Some(trait_def_id) = cx.tcx.get_diagnostic_item(trait_sym) else { + return false; + }; + implements_trait(cx, ty, trait_def_id, &[parent_ty.into()]) + } + + fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + !matches_value(cx, parent_ty, ty) + && !matches_ref(cx, Mutability::Not, parent_ty, ty) + && !matches_ref(cx, Mutability::Mut, parent_ty, ty) + } + + match self { + Self::Value => matches_value(cx, parent_ty, ty), + Self::Ref => matches_ref(cx, Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty), + Self::RefMut => matches_ref(cx, Mutability::Mut, parent_ty, ty), + Self::No => matches_none(cx, parent_ty, ty), + } + } + + #[must_use] + pub(super) fn description(self) -> &'static str { + match self { + Self::Value => "`self` by value", + Self::Ref => "`self` by reference", + Self::RefMut => "`self` by mutable reference", + Self::No => "no `self`", + } + } +} diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs index a98cfff8bfbd1..6190c43578e9b 100644 --- a/clippy_lints/src/methods/map_identity.rs +++ b/clippy_lints/src/methods/map_identity.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; use clippy_utils::{is_expr_untyped_identity_function, is_mutable, is_trait_method, path_to_local_with_projections}; @@ -27,48 +27,46 @@ pub(super) fn check( && is_expr_untyped_identity_function(cx, map_arg) && let Some(call_span) = expr.span.trim_start(caller.span) { - let main_sugg = (call_span, String::new()); - let mut app = if is_copy(cx, caller_ty) { - // there is technically a behavioral change here for `Copy` iterators, where - // `iter.map(|x| x).next()` would mutate a temporary copy of the iterator and - // changing it to `iter.next()` mutates iter directly - Applicability::Unspecified - } else { - Applicability::MachineApplicable - }; + span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| { + let main_sugg = (call_span, String::new()); + let mut app = if is_copy(cx, caller_ty) { + // there is technically a behavioral change here for `Copy` iterators, where + // `iter.map(|x| x).next()` would mutate a temporary copy of the iterator and + // changing it to `iter.next()` mutates iter directly + Applicability::Unspecified + } else { + Applicability::MachineApplicable + }; - let needs_to_be_mutable = cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr(); - if needs_to_be_mutable && !is_mutable(cx, caller) { - if let Some(hir_id) = path_to_local_with_projections(caller) - && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) - && let PatKind::Binding(_, _, ident, _) = pat.kind - { - // We can reach the binding -- suggest making it mutable - let suggs = vec![main_sugg, (ident.span.shrink_to_lo(), String::from("mut "))]; + let needs_to_be_mutable = cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr(); + if needs_to_be_mutable && !is_mutable(cx, caller) { + if let Some(hir_id) = path_to_local_with_projections(caller) + && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) + && let PatKind::Binding(_, _, ident, _) = pat.kind + { + // We can reach the binding -- suggest making it mutable + let suggs = vec![main_sugg, (ident.span.shrink_to_lo(), String::from("mut "))]; - let ident = snippet_with_applicability(cx.sess(), ident.span, "_", &mut app); + let ident = snippet_with_applicability(cx.sess(), ident.span, "_", &mut app); - span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| { diag.multipart_suggestion( format!("remove the call to `{name}`, and make `{ident}` mutable"), suggs, app, ); - }); - } else { - // If we can't make the binding mutable, prevent the suggestion from being automatically applied, - // and add a complementary help message. - app = Applicability::Unspecified; - - let method_requiring_mut = if let Node::Expr(expr) = cx.tcx.parent_hir_node(expr.hir_id) - && let ExprKind::MethodCall(method, ..) = expr.kind - { - Some(method.ident) } else { - None - }; + // If we can't make the binding mutable, prevent the suggestion from being automatically applied, + // and add a complementary help message. + app = Applicability::Unspecified; + + let method_requiring_mut = if let Node::Expr(expr) = cx.tcx.parent_hir_node(expr.hir_id) + && let ExprKind::MethodCall(method, ..) = expr.kind + { + Some(method.ident) + } else { + None + }; - span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| { diag.span_suggestion(main_sugg.0, format!("remove the call to `{name}`"), main_sugg.1, app); let note = if let Some(method_requiring_mut) = method_requiring_mut { @@ -77,18 +75,10 @@ pub(super) fn check( "this must be made mutable".to_string() }; diag.span_note(caller.span, note); - }); + } + } else { + diag.span_suggestion(main_sugg.0, format!("remove the call to `{name}`"), main_sugg.1, app); } - } else { - span_lint_and_sugg( - cx, - MAP_IDENTITY, - main_sugg.0, - MSG, - format!("remove the call to `{name}`"), - main_sugg.1, - app, - ); - } + }); } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8679689c8ad4d..b0b4e5eedb084 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -55,6 +55,7 @@ mod iter_skip_zero; mod iter_with_drain; mod iterator_step_by_zero; mod join_absolute_paths; +mod lib; mod manual_c_str_literals; mod manual_contains; mod manual_inspect; @@ -79,6 +80,7 @@ mod needless_character_iteration; mod needless_collect; mod needless_option_as_deref; mod needless_option_take; +mod new_ret_no_self; mod no_effect_replace; mod obfuscated_if_else; mod ok_expect; @@ -102,9 +104,8 @@ mod return_and_then; mod search_is_some; mod seek_from_current; mod seek_to_start_instead_of_rewind; +mod should_implement_trait; mod single_char_add_str; -mod single_char_insert_string; -mod single_char_push_string; mod skip_while_next; mod sliced_string_as_bytes; mod stable_sort_primitive; @@ -148,20 +149,16 @@ mod zst_offset; use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::FormatArgsStorage; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty, sym}; +use clippy_utils::{contains_return, is_trait_method, iter_input_pats, peel_blocks, sym}; pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; -use rustc_abi::ExternAbi; use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; -use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; +use rustc_hir::{self as hir, Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{self, TraitRef, Ty}; +use rustc_middle::ty::TraitRef; use rustc_session::impl_lint_pass; -use rustc_span::{Span, Symbol, kw}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -477,6 +474,9 @@ declare_clippy_lint! { /// ### What it does /// Checks for usage of `ok().expect(..)`. /// + /// Note: This lint only triggers for code marked compatible + /// with versions of the compiler older than Rust 1.82.0. + /// /// ### Why is this bad? /// Because you usually call `expect()` on the `Result` /// directly to get a better error message. @@ -1080,9 +1080,9 @@ declare_clippy_lint! { /// `T` implements `ToString` directly (like `&&str` or `&&String`). /// /// ### Why is this bad? - /// This bypasses the specialized implementation of - /// `ToString` and instead goes through the more expensive string formatting - /// facilities. + /// In versions of the compiler before Rust 1.82.0, this bypasses the specialized + /// implementation of`ToString` and instead goes through the more expensive string + /// formatting facilities. /// /// ### Example /// ```no_run @@ -4462,7 +4462,7 @@ declare_clippy_lint! { /// Checks for calls to `Read::bytes` on types which don't implement `BufRead`. /// /// ### Why is this bad? - /// The default implementation calls `read` for each byte, which can be very inefficient for data that’s not in memory, such as `File`. + /// The default implementation calls `read` for each byte, which can be very inefficient for data that's not in memory, such as `File`. /// /// ### Example /// ```no_run @@ -4856,7 +4856,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { }, ExprKind::MethodCall(method_call, receiver, args, _) => { let method_span = method_call.ident.span; - or_fun_call::check(cx, expr, method_span, method_call.ident.name, receiver, args); + or_fun_call::check(cx, expr, method_span, method_call.ident.name, receiver, args, self.msrv); expect_fun_call::check( cx, &self.format_args, @@ -4868,7 +4868,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ); clone_on_copy::check(cx, expr, method_call.ident.name, receiver, args); clone_on_ref_ptr::check(cx, expr, method_call.ident.name, receiver, args); - inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args); + inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args, self.msrv); single_char_add_str::check(cx, expr, receiver, args); into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver); unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv); @@ -4891,48 +4891,17 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if impl_item.span.in_external_macro(cx.sess().source_map()) { return; } - let name = impl_item.ident.name; - let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id; - let item = cx.tcx.hir_expect_item(parent); - let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); - let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind { + let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id; + let item = cx.tcx.hir_expect_item(parent); + let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); + let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); + let method_sig = cx.tcx.fn_sig(impl_item.owner_id).instantiate_identity(); let method_sig = cx.tcx.instantiate_bound_regions_with_erased(method_sig); let first_arg_ty_opt = method_sig.inputs().iter().next().copied(); - // if this impl block implements a trait, lint in trait definition instead - if !implements_trait && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) { - // check missing trait implementations - for method_config in &TRAIT_METHODS { - if name == method_config.method_name - && sig.decl.inputs.len() == method_config.param_count - && method_config.output_type.matches(&sig.decl.output) - // in case there is no first arg, since we already have checked the number of arguments - // it's should be always true - && first_arg_ty_opt.is_none_or(|first_arg_ty| method_config - .self_kind.matches(cx, self_ty, first_arg_ty) - ) - && fn_header_equals(method_config.fn_header, sig.header) - && method_config.lifetime_param_cond(impl_item) - { - span_lint_and_help( - cx, - SHOULD_IMPLEMENT_TRAIT, - impl_item.span, - format!( - "method `{}` can be confused for the standard trait method `{}::{}`", - method_config.method_name, method_config.trait_name, method_config.method_name - ), - None, - format!( - "consider implementing the trait `{}` or choosing a less ambiguous method name", - method_config.trait_name - ), - ); - } - } - } + should_implement_trait::check_impl_item(cx, impl_item, self_ty, implements_trait, first_arg_ty_opt, sig); if sig.decl.implicit_self.has_implicit_self() && !(self.avoid_breaking_exported_api @@ -4942,7 +4911,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { { wrong_self_convention::check( cx, - name, + impl_item.ident.name, self_ty, first_arg_ty, first_arg.pat.span, @@ -4950,28 +4919,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { false, ); } - } - // if this impl block implements a trait, lint in trait definition instead - if implements_trait { - return; - } - - if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { - let ret_ty = return_ty(cx, impl_item.owner_id); - - if contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) { - return; - } - - if name == sym::new && ret_ty != self_ty { - span_lint( - cx, - NEW_RET_NO_SELF, - impl_item.span, - "methods called `new` usually return `Self`", - ); - } + new_ret_no_self::check_impl_item(cx, impl_item, self_ty, implements_trait); } } @@ -4980,41 +4929,30 @@ impl<'tcx> LateLintPass<'tcx> for Methods { return; } - if let TraitItemKind::Fn(ref sig, _) = item.kind - && sig.decl.implicit_self.has_implicit_self() - && let Some(first_arg_hir_ty) = sig.decl.inputs.first() - && let Some(&first_arg_ty) = cx - .tcx - .fn_sig(item.owner_id) - .instantiate_identity() - .inputs() - .skip_binder() - .first() - { - let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); - wrong_self_convention::check( - cx, - item.ident.name, - self_ty, - first_arg_ty, - first_arg_hir_ty.span, - false, - true, - ); - } + if let TraitItemKind::Fn(ref sig, _) = item.kind { + if sig.decl.implicit_self.has_implicit_self() + && let Some(first_arg_hir_ty) = sig.decl.inputs.first() + && let Some(&first_arg_ty) = cx + .tcx + .fn_sig(item.owner_id) + .instantiate_identity() + .inputs() + .skip_binder() + .first() + { + let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); + wrong_self_convention::check( + cx, + item.ident.name, + self_ty, + first_arg_ty, + first_arg_hir_ty.span, + false, + true, + ); + } - if item.ident.name == sym::new - && let TraitItemKind::Fn(_, _) = item.kind - && let ret_ty = return_ty(cx, item.owner_id) - && let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty() - && !ret_ty.contains(self_ty) - { - span_lint( - cx, - NEW_RET_NO_SELF, - item.span, - "methods called `new` usually return `Self`", - ); + new_ret_no_self::check_trait_item(cx, item); } } } @@ -5369,7 +5307,9 @@ impl Methods { iter_overeager_cloned::Op::LaterCloned, false, ), - (sym::filter, [arg]) => filter_next::check(cx, expr, recv2, arg), + (sym::filter, [arg]) => { + filter_next::check(cx, expr, recv2, arg, filter_next::Direction::Forward); + }, (sym::filter_map, [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv), (sym::iter, []) => iter_next_slice::check(cx, expr, recv2), (sym::skip, [arg]) => iter_skip_next::check(cx, expr, recv2, arg), @@ -5379,6 +5319,14 @@ impl Methods { } } }, + (sym::next_back, []) => { + if let Some((name2, recv2, args2, _, _)) = method_call(recv) + && let (sym::filter, [arg]) = (name2, args2) + && self.msrv.meets(cx, msrvs::DOUBLE_ENDED_ITERATOR_RFIND) + { + filter_next::check(cx, expr, recv2, arg, filter_next::Direction::Backward); + } + }, (sym::nth, [n_arg]) => match method_call(recv) { Some((sym::bytes, recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg), Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( @@ -5712,183 +5660,3 @@ fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExpr lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info); lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info); } - -const FN_HEADER: hir::FnHeader = hir::FnHeader { - safety: hir::HeaderSafety::Normal(hir::Safety::Safe), - constness: hir::Constness::NotConst, - asyncness: hir::IsAsync::NotAsync, - abi: ExternAbi::Rust, -}; - -struct ShouldImplTraitCase { - trait_name: &'static str, - method_name: Symbol, - param_count: usize, - fn_header: hir::FnHeader, - // implicit self kind expected (none, self, &self, ...) - self_kind: SelfKind, - // checks against the output type - output_type: OutType, - // certain methods with explicit lifetimes can't implement the equivalent trait method - lint_explicit_lifetime: bool, -} -impl ShouldImplTraitCase { - const fn new( - trait_name: &'static str, - method_name: Symbol, - param_count: usize, - fn_header: hir::FnHeader, - self_kind: SelfKind, - output_type: OutType, - lint_explicit_lifetime: bool, - ) -> ShouldImplTraitCase { - ShouldImplTraitCase { - trait_name, - method_name, - param_count, - fn_header, - self_kind, - output_type, - lint_explicit_lifetime, - } - } - - fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool { - self.lint_explicit_lifetime - || !impl_item.generics.params.iter().any(|p| { - matches!( - p.kind, - hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Explicit - } - ) - }) - } -} - -#[rustfmt::skip] -const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ - ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, FN_HEADER, SelfKind::Ref, OutType::Any, true), - ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, FN_HEADER, SelfKind::Ref, OutType::Any, true), - ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true), - ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true), - ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, FN_HEADER, SelfKind::No, OutType::Any, true), - ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true), - ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true), - ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), - ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), - ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), - ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, FN_HEADER, SelfKind::Value, OutType::Any, true), -]; - -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -enum SelfKind { - Value, - Ref, - RefMut, - No, // When we want the first argument type to be different than `Self` -} - -impl SelfKind { - fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { - fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { - if ty == parent_ty { - true - } else if let Some(boxed_ty) = ty.boxed_ty() { - boxed_ty == parent_ty - } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) { - if let ty::Adt(_, args) = ty.kind() { - args.types().next() == Some(parent_ty) - } else { - false - } - } else { - false - } - } - - fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { - if let ty::Ref(_, t, m) = *ty.kind() { - return m == mutability && t == parent_ty; - } - - let trait_sym = match mutability { - hir::Mutability::Not => sym::AsRef, - hir::Mutability::Mut => sym::AsMut, - }; - - let Some(trait_def_id) = cx.tcx.get_diagnostic_item(trait_sym) else { - return false; - }; - implements_trait(cx, ty, trait_def_id, &[parent_ty.into()]) - } - - fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { - !matches_value(cx, parent_ty, ty) - && !matches_ref(cx, hir::Mutability::Not, parent_ty, ty) - && !matches_ref(cx, hir::Mutability::Mut, parent_ty, ty) - } - - match self { - Self::Value => matches_value(cx, parent_ty, ty), - Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty), - Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty), - Self::No => matches_none(cx, parent_ty, ty), - } - } - - #[must_use] - fn description(self) -> &'static str { - match self { - Self::Value => "`self` by value", - Self::Ref => "`self` by reference", - Self::RefMut => "`self` by mutable reference", - Self::No => "no `self`", - } - } -} - -#[derive(Clone, Copy)] -enum OutType { - Unit, - Bool, - Any, - Ref, -} - -impl OutType { - fn matches(self, ty: &hir::FnRetTy<'_>) -> bool { - let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[])); - match (self, ty) { - (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true, - (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true, - (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true, - (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true, - (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Ref(_, _)), - _ => false, - } - } -} - -fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool { - expected.constness == actual.constness && expected.safety == actual.safety && expected.asyncness == actual.asyncness -} diff --git a/clippy_lints/src/methods/new_ret_no_self.rs b/clippy_lints/src/methods/new_ret_no_self.rs new file mode 100644 index 0000000000000..2aa28fbb5f406 --- /dev/null +++ b/clippy_lints/src/methods/new_ret_no_self.rs @@ -0,0 +1,46 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::return_ty; +use clippy_utils::ty::contains_ty_adt_constructor_opaque; +use rustc_hir::{ImplItem, TraitItem}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_span::sym; + +use super::NEW_RET_NO_SELF; + +pub(super) fn check_impl_item<'tcx>( + cx: &LateContext<'tcx>, + impl_item: &'tcx ImplItem<'_>, + self_ty: Ty<'tcx>, + implements_trait: bool, +) { + // if this impl block implements a trait, lint in trait definition instead + if !implements_trait + && impl_item.ident.name == sym::new + && let ret_ty = return_ty(cx, impl_item.owner_id) + && ret_ty != self_ty + && !contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) + { + span_lint( + cx, + NEW_RET_NO_SELF, + impl_item.span, + "methods called `new` usually return `Self`", + ); + } +} + +pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'tcx>) { + if trait_item.ident.name == sym::new + && let ret_ty = return_ty(cx, trait_item.owner_id) + && let self_ty = ty::TraitRef::identity(cx.tcx, trait_item.owner_id.to_def_id()).self_ty() + && !ret_ty.contains(self_ty) + { + span_lint( + cx, + NEW_RET_NO_SELF, + trait_item.span, + "methods called `new` usually return `Self`", + ); + } +} diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 71b2f251eded6..04e4503e4097f 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -3,6 +3,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; use clippy_utils::higher::VecArgs; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; use clippy_utils::visitors::for_each_expr; @@ -18,7 +19,6 @@ use {rustc_ast as ast, rustc_hir as hir}; use super::{OR_FUN_CALL, UNWRAP_OR_DEFAULT}; /// Checks for the `OR_FUN_CALL` lint. -#[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, @@ -26,185 +26,8 @@ pub(super) fn check<'tcx>( name: Symbol, receiver: &'tcx hir::Expr<'_>, args: &'tcx [hir::Expr<'_>], + msrv: Msrv, ) { - /// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`, - /// `or_insert(T::new())` or `or_insert(T::default())`. - /// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`, - /// `or_insert_with(T::new)` or `or_insert_with(T::default)`. - fn check_unwrap_or_default( - cx: &LateContext<'_>, - name: Symbol, - receiver: &hir::Expr<'_>, - fun: &hir::Expr<'_>, - call_expr: Option<&hir::Expr<'_>>, - span: Span, - method_span: Span, - ) -> bool { - if !expr_type_is_certain(cx, receiver) { - return false; - } - - let is_new = |fun: &hir::Expr<'_>| { - if let hir::ExprKind::Path(ref qpath) = fun.kind { - let path = last_path_segment(qpath).ident.name; - matches!(path, sym::new) - } else { - false - } - }; - - let output_type_implements_default = |fun| { - let fun_ty = cx.typeck_results().expr_ty(fun); - if let ty::FnDef(def_id, args) = fun_ty.kind() { - let output_ty = cx.tcx.fn_sig(def_id).instantiate(cx.tcx, args).skip_binder().output(); - cx.tcx - .get_diagnostic_item(sym::Default) - .is_some_and(|default_trait_id| implements_trait(cx, output_ty, default_trait_id, &[])) - } else { - false - } - }; - - let sugg = match (name, call_expr.is_some()) { - (sym::unwrap_or, true) | (sym::unwrap_or_else, false) => sym::unwrap_or_default, - (sym::or_insert, true) | (sym::or_insert_with, false) => sym::or_default, - _ => return false, - }; - - let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); - let Some(suggested_method_def_id) = receiver_ty.ty_adt_def().and_then(|adt_def| { - cx.tcx - .inherent_impls(adt_def.did()) - .iter() - .flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg)) - .find_map(|assoc| { - if assoc.is_method() && cx.tcx.fn_sig(assoc.def_id).skip_binder().inputs().skip_binder().len() == 1 - { - Some(assoc.def_id) - } else { - None - } - }) - }) else { - return false; - }; - let in_sugg_method_implementation = { - matches!( - suggested_method_def_id.as_local(), - Some(local_def_id) if local_def_id == cx.tcx.hir_get_parent_item(receiver.hir_id).def_id - ) - }; - if in_sugg_method_implementation { - return false; - } - - // `.unwrap_or(vec![])` is as readable as `.unwrap_or_default()`. And if the expression is a - // non-empty `Vec`, then it will not be a default value anyway. Bail out in all cases. - if call_expr.and_then(|call_expr| VecArgs::hir(cx, call_expr)).is_some() { - return false; - } - - // needs to target Default::default in particular or be *::new and have a Default impl - // available - if (is_new(fun) && output_type_implements_default(fun)) - || match call_expr { - Some(call_expr) => is_default_equivalent(cx, call_expr), - None => is_default_equivalent_call(cx, fun, None) || closure_body_returns_empty_to_string(cx, fun), - } - { - span_lint_and_sugg( - cx, - UNWRAP_OR_DEFAULT, - method_span.with_hi(span.hi()), - format!("use of `{name}` to construct default value"), - "try", - format!("{sugg}()"), - Applicability::MachineApplicable, - ); - - true - } else { - false - } - } - - /// Checks for `*or(foo())`. - #[expect(clippy::too_many_arguments)] - fn check_or_fn_call<'tcx>( - cx: &LateContext<'tcx>, - name: Symbol, - method_span: Span, - self_expr: &hir::Expr<'_>, - arg: &'tcx hir::Expr<'_>, - // `Some` if fn has second argument - second_arg: Option<&hir::Expr<'_>>, - span: Span, - // None if lambda is required - fun_span: Option, - ) -> bool { - // (path, fn_has_argument, methods, suffix) - const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 7] = [ - (sym::BTreeEntry, false, &[sym::or_insert], "with"), - (sym::HashMapEntry, false, &[sym::or_insert], "with"), - ( - sym::Option, - false, - &[sym::map_or, sym::ok_or, sym::or, sym::unwrap_or], - "else", - ), - (sym::Option, false, &[sym::get_or_insert], "with"), - (sym::Option, true, &[sym::and], "then"), - (sym::Result, true, &[sym::map_or, sym::or, sym::unwrap_or], "else"), - (sym::Result, true, &[sym::and], "then"), - ]; - - if KNOW_TYPES.iter().any(|k| k.2.contains(&name)) - && switch_to_lazy_eval(cx, arg) - && !contains_return(arg) - && let self_ty = cx.typeck_results().expr_ty(self_expr) - && let Some(&(_, fn_has_arguments, _, suffix)) = KNOW_TYPES - .iter() - .find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0) && i.2.contains(&name)) - { - let ctxt = span.ctxt(); - let mut app = Applicability::HasPlaceholders; - let sugg = { - let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { - (false, Some(fun_span)) => (fun_span, false), - _ => (arg.span, true), - }; - - let snip = snippet_with_context(cx, snippet_span, ctxt, "..", &mut app).0; - let snip = if use_lambda { - let l_arg = if fn_has_arguments { "_" } else { "" }; - format!("|{l_arg}| {snip}") - } else { - snip.into_owned() - }; - - if let Some(f) = second_arg { - let f = snippet_with_context(cx, f.span, ctxt, "..", &mut app).0; - format!("{snip}, {f}") - } else { - snip - } - }; - let span_replace_word = method_span.with_hi(span.hi()); - span_lint_and_sugg( - cx, - OR_FUN_CALL, - span_replace_word, - format!("function call inside of `{name}`"), - "try", - format!("{name}_{suffix}({sugg})"), - app, - ); - true - } else { - false - } - } - if let [arg] = args { let inner_arg = peel_blocks(arg); for_each_expr(cx, inner_arg, |ex| { @@ -224,11 +47,11 @@ pub(super) fn check<'tcx>( }; (!inner_fun_has_args && !is_nested_expr - && check_unwrap_or_default(cx, name, receiver, fun, Some(ex), expr.span, method_span)) + && check_unwrap_or_default(cx, name, receiver, fun, Some(ex), expr.span, method_span, msrv)) || check_or_fn_call(cx, name, method_span, receiver, arg, None, expr.span, fun_span) }, hir::ExprKind::Path(..) | hir::ExprKind::Closure(..) if !is_nested_expr => { - check_unwrap_or_default(cx, name, receiver, ex, None, expr.span, method_span) + check_unwrap_or_default(cx, name, receiver, ex, None, expr.span, method_span, msrv) }, hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { check_or_fn_call(cx, name, method_span, receiver, arg, None, expr.span, None) @@ -272,6 +95,191 @@ pub(super) fn check<'tcx>( } } +/// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`, +/// `or_insert(T::new())` or `or_insert(T::default())`. +/// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`, +/// `or_insert_with(T::new)` or `or_insert_with(T::default)`. +#[expect(clippy::too_many_arguments)] +fn check_unwrap_or_default( + cx: &LateContext<'_>, + name: Symbol, + receiver: &hir::Expr<'_>, + fun: &hir::Expr<'_>, + call_expr: Option<&hir::Expr<'_>>, + span: Span, + method_span: Span, + msrv: Msrv, +) -> bool { + let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); + + // Check MSRV, but only for `Result::unwrap_or_default` + if is_type_diagnostic_item(cx, receiver_ty, sym::Result) && !msrv.meets(cx, msrvs::RESULT_UNWRAP_OR_DEFAULT) { + return false; + } + + if !expr_type_is_certain(cx, receiver) { + return false; + } + + let is_new = |fun: &hir::Expr<'_>| { + if let hir::ExprKind::Path(ref qpath) = fun.kind { + let path = last_path_segment(qpath).ident.name; + matches!(path, sym::new) + } else { + false + } + }; + + let output_type_implements_default = |fun| { + let fun_ty = cx.typeck_results().expr_ty(fun); + if let ty::FnDef(def_id, args) = fun_ty.kind() { + let output_ty = cx.tcx.fn_sig(def_id).instantiate(cx.tcx, args).skip_binder().output(); + cx.tcx + .get_diagnostic_item(sym::Default) + .is_some_and(|default_trait_id| implements_trait(cx, output_ty, default_trait_id, &[])) + } else { + false + } + }; + + let sugg = match (name, call_expr.is_some()) { + (sym::unwrap_or, true) | (sym::unwrap_or_else, false) => sym::unwrap_or_default, + (sym::or_insert, true) | (sym::or_insert_with, false) => sym::or_default, + _ => return false, + }; + + let Some(suggested_method_def_id) = receiver_ty.ty_adt_def().and_then(|adt_def| { + cx.tcx + .inherent_impls(adt_def.did()) + .iter() + .flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg)) + .find_map(|assoc| { + if assoc.is_method() && cx.tcx.fn_sig(assoc.def_id).skip_binder().inputs().skip_binder().len() == 1 { + Some(assoc.def_id) + } else { + None + } + }) + }) else { + return false; + }; + let in_sugg_method_implementation = { + matches!( + suggested_method_def_id.as_local(), + Some(local_def_id) if local_def_id == cx.tcx.hir_get_parent_item(receiver.hir_id).def_id + ) + }; + if in_sugg_method_implementation { + return false; + } + + // `.unwrap_or(vec![])` is as readable as `.unwrap_or_default()`. And if the expression is a + // non-empty `Vec`, then it will not be a default value anyway. Bail out in all cases. + if call_expr.and_then(|call_expr| VecArgs::hir(cx, call_expr)).is_some() { + return false; + } + + // needs to target Default::default in particular or be *::new and have a Default impl + // available + if (is_new(fun) && output_type_implements_default(fun)) + || match call_expr { + Some(call_expr) => is_default_equivalent(cx, call_expr), + None => is_default_equivalent_call(cx, fun, None) || closure_body_returns_empty_to_string(cx, fun), + } + { + span_lint_and_sugg( + cx, + UNWRAP_OR_DEFAULT, + method_span.with_hi(span.hi()), + format!("use of `{name}` to construct default value"), + "try", + format!("{sugg}()"), + Applicability::MachineApplicable, + ); + + true + } else { + false + } +} + +/// Checks for `*or(foo())`. +#[expect(clippy::too_many_arguments)] +fn check_or_fn_call<'tcx>( + cx: &LateContext<'tcx>, + name: Symbol, + method_span: Span, + self_expr: &hir::Expr<'_>, + arg: &'tcx hir::Expr<'_>, + // `Some` if fn has second argument + second_arg: Option<&hir::Expr<'_>>, + span: Span, + // None if lambda is required + fun_span: Option, +) -> bool { + // (path, fn_has_argument, methods, suffix) + const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 7] = [ + (sym::BTreeEntry, false, &[sym::or_insert], "with"), + (sym::HashMapEntry, false, &[sym::or_insert], "with"), + ( + sym::Option, + false, + &[sym::map_or, sym::ok_or, sym::or, sym::unwrap_or], + "else", + ), + (sym::Option, false, &[sym::get_or_insert], "with"), + (sym::Option, true, &[sym::and], "then"), + (sym::Result, true, &[sym::map_or, sym::or, sym::unwrap_or], "else"), + (sym::Result, true, &[sym::and], "then"), + ]; + + if KNOW_TYPES.iter().any(|k| k.2.contains(&name)) + && switch_to_lazy_eval(cx, arg) + && !contains_return(arg) + && let self_ty = cx.typeck_results().expr_ty(self_expr) + && let Some(&(_, fn_has_arguments, _, suffix)) = KNOW_TYPES + .iter() + .find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0) && i.2.contains(&name)) + { + let ctxt = span.ctxt(); + let mut app = Applicability::HasPlaceholders; + let sugg = { + let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { + (false, Some(fun_span)) => (fun_span, false), + _ => (arg.span, true), + }; + + let snip = snippet_with_context(cx, snippet_span, ctxt, "..", &mut app).0; + let snip = if use_lambda { + let l_arg = if fn_has_arguments { "_" } else { "" }; + format!("|{l_arg}| {snip}") + } else { + snip.into_owned() + }; + + if let Some(f) = second_arg { + let f = snippet_with_context(cx, f.span, ctxt, "..", &mut app).0; + format!("{snip}, {f}") + } else { + snip + } + }; + let span_replace_word = method_span.with_hi(span.hi()); + span_lint_and_sugg( + cx, + OR_FUN_CALL, + span_replace_word, + format!("function call inside of `{name}`"), + "try", + format!("{name}_{suffix}({sugg})"), + app, + ); + true + } else { + false + } +} + fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool { if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = e.kind { let body = cx.tcx.hir_body(body); diff --git a/clippy_lints/src/methods/range_zip_with_len.rs b/clippy_lints/src/methods/range_zip_with_len.rs index 3a5e321720865..e13df18333e46 100644 --- a/clippy_lints/src/methods/range_zip_with_len.rs +++ b/clippy_lints/src/methods/range_zip_with_len.rs @@ -1,10 +1,9 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; -use clippy_utils::{SpanlessEq, higher, is_integer_const, is_trait_method}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{SpanRangeExt as _, snippet_with_applicability}; +use clippy_utils::{SpanlessEq, get_parent_expr, higher, is_integer_const, is_trait_method, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_hir::{Expr, ExprKind, Node, Pat, PatKind, QPath}; use rustc_lint::LateContext; -use rustc_span::sym; use super::RANGE_ZIP_WITH_LEN; @@ -21,14 +20,93 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' && let ExprKind::Path(QPath::Resolved(_, len_path)) = len_recv.kind && SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments) { - span_lint_and_sugg( + span_lint_and_then( cx, RANGE_ZIP_WITH_LEN, expr.span, "using `.zip()` with a range and `.len()`", - "try", - format!("{}.iter().enumerate()", snippet(cx, recv.span, "_")), - Applicability::MachineApplicable, + |diag| { + // If the iterator content is consumed by a pattern with exactly two elements, swap + // the order of those elements. Otherwise, the suggestion will be marked as + // `Applicability::MaybeIncorrect` (because it will be), and a note will be added + // to the diagnostic to underline the swapping of the index and the content. + let pat = methods_pattern(cx, expr).or_else(|| for_loop_pattern(cx, expr)); + let invert_bindings = if let Some(pat) = pat + && pat.span.eq_ctxt(expr.span) + && let PatKind::Tuple([first, second], _) = pat.kind + { + Some((first.span, second.span)) + } else { + None + }; + let mut app = Applicability::MachineApplicable; + let mut suggestions = vec![( + expr.span, + format!( + "{}.iter().enumerate()", + snippet_with_applicability(cx, recv.span, "_", &mut app) + ), + )]; + if let Some((left, right)) = invert_bindings + && let Some(snip_left) = left.get_source_text(cx) + && let Some(snip_right) = right.get_source_text(cx) + { + suggestions.extend([(left, snip_right.to_string()), (right, snip_left.to_string())]); + } else { + app = Applicability::MaybeIncorrect; + } + diag.multipart_suggestion("use", suggestions, app); + if app != Applicability::MachineApplicable { + diag.note("the order of the element and the index will be swapped"); + } + }, ); } } + +/// If `expr` is the argument of a `for` loop, return the loop pattern. +fn for_loop_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Pat<'tcx>> { + cx.tcx.hir_parent_iter(expr.hir_id).find_map(|(_, node)| { + if let Node::Expr(ancestor_expr) = node + && let Some(for_loop) = higher::ForLoop::hir(ancestor_expr) + && for_loop.arg.hir_id == expr.hir_id + { + Some(for_loop.pat) + } else { + None + } + }) +} + +/// If `expr` is the receiver of an `Iterator` method which consumes the iterator elements and feed +/// them to a closure, return the pattern of the closure. +fn methods_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Pat<'tcx>> { + if let Some(parent_expr) = get_parent_expr(cx, expr) + && is_trait_method(cx, expr, sym::Iterator) + && let ExprKind::MethodCall(method, recv, [arg], _) = parent_expr.kind + && recv.hir_id == expr.hir_id + && matches!( + method.ident.name, + sym::all + | sym::any + | sym::filter_map + | sym::find_map + | sym::flat_map + | sym::for_each + | sym::is_partitioned + | sym::is_sorted_by_key + | sym::map + | sym::map_while + | sym::position + | sym::rposition + | sym::try_for_each + ) + && let ExprKind::Closure(closure) = arg.kind + && let body = cx.tcx.hir_body(closure.body) + && let [param] = body.params + { + Some(param.pat) + } else { + None + } +} diff --git a/clippy_lints/src/methods/repeat_once.rs b/clippy_lints/src/methods/repeat_once.rs index 7837517ed5d8a..9111604ef53b7 100644 --- a/clippy_lints/src/methods/repeat_once.rs +++ b/clippy_lints/src/methods/repeat_once.rs @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>( recv: &'tcx Expr<'_>, repeat_arg: &'tcx Expr<'_>, ) { - if ConstEvalCtxt::new(cx).eval(repeat_arg) == Some(Constant::Int(1)) { + if ConstEvalCtxt::new(cx).eval_local(repeat_arg, expr.span.ctxt()) == Some(Constant::Int(1)) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); if ty.is_str() { span_lint_and_sugg( diff --git a/clippy_lints/src/methods/should_implement_trait.rs b/clippy_lints/src/methods/should_implement_trait.rs new file mode 100644 index 0000000000000..599ff696f6aed --- /dev/null +++ b/clippy_lints/src/methods/should_implement_trait.rs @@ -0,0 +1,156 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{is_bool, sym}; +use rustc_abi::ExternAbi; +use rustc_hir::{self as hir, FnRetTy, FnSig, GenericParamKind, ImplItem, LifetimeParamKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_span::edition::Edition::{self, Edition2015, Edition2021}; +use rustc_span::{Symbol, kw}; + +use super::SHOULD_IMPLEMENT_TRAIT; +use super::lib::SelfKind; + +pub(super) fn check_impl_item<'tcx>( + cx: &LateContext<'tcx>, + impl_item: &'tcx ImplItem<'_>, + self_ty: Ty<'tcx>, + impl_implements_trait: bool, + first_arg_ty_opt: Option>, + sig: &FnSig<'_>, +) { + // if this impl block implements a trait, lint in trait definition instead + if !impl_implements_trait && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) + // check missing trait implementations + && let Some(method_config) = TRAIT_METHODS.iter().find(|case| case.method_name == impl_item.ident.name) + && sig.decl.inputs.len() == method_config.param_count + && method_config.output_type.matches(&sig.decl.output) + // in case there is no first arg, since we already have checked the number of arguments + // it's should be always true + && first_arg_ty_opt + .is_none_or(|first_arg_ty| method_config.self_kind.matches(cx, self_ty, first_arg_ty)) + && sig.header.is_safe() + && !sig.header.is_const() + && !sig.header.is_async() + && sig.header.abi == ExternAbi::Rust + && method_config.lifetime_param_cond(impl_item) + && method_config.in_prelude_since <= cx.tcx.sess.edition() + { + span_lint_and_help( + cx, + SHOULD_IMPLEMENT_TRAIT, + impl_item.span, + format!( + "method `{}` can be confused for the standard trait method `{}::{}`", + method_config.method_name, method_config.trait_name, method_config.method_name + ), + None, + format!( + "consider implementing the trait `{}` or choosing a less ambiguous method name", + method_config.trait_name + ), + ); + } +} + +struct ShouldImplTraitCase { + trait_name: &'static str, + method_name: Symbol, + param_count: usize, + // implicit self kind expected (none, self, &self, ...) + self_kind: SelfKind, + // checks against the output type + output_type: OutType, + // certain methods with explicit lifetimes can't implement the equivalent trait method + lint_explicit_lifetime: bool, + in_prelude_since: Edition, +} + +impl ShouldImplTraitCase { + const fn new( + trait_name: &'static str, + method_name: Symbol, + param_count: usize, + self_kind: SelfKind, + output_type: OutType, + lint_explicit_lifetime: bool, + in_prelude_since: Edition, + ) -> ShouldImplTraitCase { + ShouldImplTraitCase { + trait_name, + method_name, + param_count, + self_kind, + output_type, + lint_explicit_lifetime, + in_prelude_since, + } + } + + fn lifetime_param_cond(&self, impl_item: &ImplItem<'_>) -> bool { + self.lint_explicit_lifetime + || !impl_item.generics.params.iter().any(|p| { + matches!( + p.kind, + GenericParamKind::Lifetime { + kind: LifetimeParamKind::Explicit + } + ) + }) + } +} + +#[rustfmt::skip] +const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ + ShouldImplTraitCase::new("std::ops::Add", sym::add, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::convert::AsMut", sym::as_mut, 1, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::convert::AsRef", sym::as_ref, 1, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::BitAnd", sym::bitand, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::BitOr", sym::bitor, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::BitXor", sym::bitxor, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::borrow::Borrow", sym::borrow, 1, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::borrow::BorrowMut", sym::borrow_mut, 1, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::clone::Clone", sym::clone, 1, SelfKind::Ref, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::cmp::Ord", sym::cmp, 2, SelfKind::Ref, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::default::Default", kw::Default, 0, SelfKind::No, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Deref", sym::deref, 1, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::DerefMut", sym::deref_mut, 1, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Div", sym::div, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Drop", sym::drop, 1, SelfKind::RefMut, OutType::Unit, true, Edition2015), + ShouldImplTraitCase::new("std::cmp::PartialEq", sym::eq, 2, SelfKind::Ref, OutType::Bool, true, Edition2015), + ShouldImplTraitCase::new("std::iter::FromIterator", sym::from_iter, 1, SelfKind::No, OutType::Any, true, Edition2021), + ShouldImplTraitCase::new("std::str::FromStr", sym::from_str, 1, SelfKind::No, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::hash::Hash", sym::hash, 2, SelfKind::Ref, OutType::Unit, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Index", sym::index, 2, SelfKind::Ref, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::ops::IndexMut", sym::index_mut, 2, SelfKind::RefMut, OutType::Ref, true, Edition2015), + ShouldImplTraitCase::new("std::iter::IntoIterator", sym::into_iter, 1, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Mul", sym::mul, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Neg", sym::neg, 1, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::iter::Iterator", sym::next, 1, SelfKind::RefMut, OutType::Any, false, Edition2015), + ShouldImplTraitCase::new("std::ops::Not", sym::not, 1, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Rem", sym::rem, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Shl", sym::shl, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Shr", sym::shr, 2, SelfKind::Value, OutType::Any, true, Edition2015), + ShouldImplTraitCase::new("std::ops::Sub", sym::sub, 2, SelfKind::Value, OutType::Any, true, Edition2015), +]; + +#[derive(Clone, Copy)] +enum OutType { + Unit, + Bool, + Any, + Ref, +} + +impl OutType { + fn matches(self, ty: &FnRetTy<'_>) -> bool { + let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[])); + match (self, ty) { + (Self::Unit, &FnRetTy::DefaultReturn(_)) => true, + (Self::Unit, &FnRetTy::Return(ty)) if is_unit(ty) => true, + (Self::Bool, &FnRetTy::Return(ty)) if is_bool(ty) => true, + (Self::Any, &FnRetTy::Return(ty)) if !is_unit(ty) => true, + (Self::Ref, &FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Ref(_, _)), + _ => false, + } + } +} diff --git a/clippy_lints/src/methods/single_char_add_str.rs b/clippy_lints/src/methods/single_char_add_str.rs index ef3d7acdc01ef..1248d1658d77e 100644 --- a/clippy_lints/src/methods/single_char_add_str.rs +++ b/clippy_lints/src/methods/single_char_add_str.rs @@ -1,14 +1,80 @@ -use crate::methods::{single_char_insert_string, single_char_push_string}; -use rustc_hir as hir; +use super::SINGLE_CHAR_ADD_STR; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal}; +use rustc_ast::BorrowKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; +use rustc_middle::ty; use rustc_span::sym; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { - match cx.tcx.get_diagnostic_name(fn_def_id) { - Some(sym::string_push_str) => single_char_push_string::check(cx, expr, receiver, args), - Some(sym::string_insert_str) => single_char_insert_string::check(cx, expr, receiver, args), - _ => {}, + let mut applicability = Applicability::MachineApplicable; + let (short_name, arg, extra) = match cx.tcx.get_diagnostic_name(fn_def_id) { + Some(sym::string_insert_str) => ( + "insert", + &args[1], + Some(|applicability| { + format!( + "{}, ", + snippet_with_applicability(cx, args[0].span, "..", applicability) + ) + }), + ), + Some(sym::string_push_str) => ("push", &args[0], None), + _ => return, + }; + + if let Some(extension_string) = str_literal_to_char_literal(cx, arg, &mut applicability, false) { + let base_string_snippet = + snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); + span_lint_and_sugg( + cx, + SINGLE_CHAR_ADD_STR, + expr.span, + format!("calling `{short_name}_str()` using a single-character string literal"), + format!("consider using `{short_name}` with a character literal"), + format!( + "{base_string_snippet}.{short_name}({}{extension_string})", + extra.map_or(String::new(), |f| f(&mut applicability)) + ), + applicability, + ); + } else if let ExprKind::AddrOf(BorrowKind::Ref, _, inner) = arg.kind + && let ExprKind::MethodCall(path_segment, method_arg, [], _) = inner.kind + && path_segment.ident.name == sym::to_string + && (is_ref_char(cx, method_arg) || is_char(cx, method_arg)) + { + let base_string_snippet = + snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); + let extension_string = match ( + snippet_with_applicability(cx, method_arg.span.source_callsite(), "_", &mut applicability), + is_ref_char(cx, method_arg), + ) { + (snippet, false) => snippet, + (snippet, true) => format!("*{snippet}").into(), + }; + span_lint_and_sugg( + cx, + SINGLE_CHAR_ADD_STR, + expr.span, + format!("calling `{short_name}_str()` using a single-character converted to string"), + format!("consider using `{short_name}` without `to_string()`"), + format!( + "{base_string_snippet}.{short_name}({}{extension_string})", + extra.map_or(String::new(), |f| f(&mut applicability)) + ), + applicability, + ); } } } + +fn is_ref_char(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + matches!(cx.typeck_results().expr_ty(expr).kind(), ty::Ref(_, ty, _) if ty.is_char()) +} + +fn is_char(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + cx.typeck_results().expr_ty(expr).is_char() +} diff --git a/clippy_lints/src/methods/single_char_insert_string.rs b/clippy_lints/src/methods/single_char_insert_string.rs deleted file mode 100644 index 4a1d25deade90..0000000000000 --- a/clippy_lints/src/methods/single_char_insert_string.rs +++ /dev/null @@ -1,67 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal}; -use rustc_ast::BorrowKind; -use rustc_errors::Applicability; -use rustc_hir::{self as hir, ExprKind}; -use rustc_lint::LateContext; - -use super::SINGLE_CHAR_ADD_STR; - -/// lint for length-1 `str`s as argument for `insert_str` -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let mut applicability = Applicability::MachineApplicable; - if let Some(extension_string) = str_literal_to_char_literal(cx, &args[1], &mut applicability, false) { - let base_string_snippet = - snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); - let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); - let sugg = format!("{base_string_snippet}.insert({pos_arg}, {extension_string})"); - span_lint_and_sugg( - cx, - SINGLE_CHAR_ADD_STR, - expr.span, - "calling `insert_str()` using a single-character string literal", - "consider using `insert` with a character literal", - sugg, - applicability, - ); - } - - if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[1].kind - && let ExprKind::MethodCall(path_segment, method_arg, [], _) = &arg.kind - && path_segment.ident.name == rustc_span::sym::to_string - && (is_ref_char(cx, method_arg) || is_char(cx, method_arg)) - { - let base_string_snippet = - snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); - let extension_string = - snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability); - let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); - let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" }; - - let sugg = format!("{base_string_snippet}.insert({pos_arg}, {deref_string}{extension_string})"); - span_lint_and_sugg( - cx, - SINGLE_CHAR_ADD_STR, - expr.span, - "calling `insert_str()` using a single-character converted to string", - "consider using `insert` without `to_string()`", - sugg, - applicability, - ); - } -} - -fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - if cx.typeck_results().expr_ty(expr).is_ref() - && let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind() - && ty.is_char() - { - return true; - } - - false -} - -fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - cx.typeck_results().expr_ty(expr).is_char() -} diff --git a/clippy_lints/src/methods/single_char_push_string.rs b/clippy_lints/src/methods/single_char_push_string.rs deleted file mode 100644 index bc271d593925e..0000000000000 --- a/clippy_lints/src/methods/single_char_push_string.rs +++ /dev/null @@ -1,65 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal}; -use rustc_ast::BorrowKind; -use rustc_errors::Applicability; -use rustc_hir::{self as hir, ExprKind}; -use rustc_lint::LateContext; - -use super::SINGLE_CHAR_ADD_STR; - -/// lint for length-1 `str`s as argument for `push_str` -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let mut applicability = Applicability::MachineApplicable; - if let Some(extension_string) = str_literal_to_char_literal(cx, &args[0], &mut applicability, false) { - let base_string_snippet = - snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); - let sugg = format!("{base_string_snippet}.push({extension_string})"); - span_lint_and_sugg( - cx, - SINGLE_CHAR_ADD_STR, - expr.span, - "calling `push_str()` using a single-character string literal", - "consider using `push` with a character literal", - sugg, - applicability, - ); - } - - if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[0].kind - && let ExprKind::MethodCall(path_segment, method_arg, [], _) = &arg.kind - && path_segment.ident.name == rustc_span::sym::to_string - && (is_ref_char(cx, method_arg) || is_char(cx, method_arg)) - { - let base_string_snippet = - snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); - let extension_string = - snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability); - let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" }; - - let sugg = format!("{base_string_snippet}.push({deref_string}{extension_string})"); - span_lint_and_sugg( - cx, - SINGLE_CHAR_ADD_STR, - expr.span, - "calling `push_str()` using a single-character converted to string", - "consider using `push` without `to_string()`", - sugg, - applicability, - ); - } -} - -fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - if cx.typeck_results().expr_ty(expr).is_ref() - && let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind() - && ty.is_char() - { - return true; - } - - false -} - -fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - cx.typeck_results().expr_ty(expr).is_char() -} diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 51dd4ac313a6d..8daa5db887acf 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -304,7 +304,7 @@ fn parse_iter_usage<'tcx>( }; }, (sym::nth | sym::skip, [idx_expr]) if cx.tcx.trait_of_assoc(did) == Some(iter_id) => { - if let Some(Constant::Int(idx)) = ConstEvalCtxt::new(cx).eval(idx_expr) { + if let Some(Constant::Int(idx)) = ConstEvalCtxt::new(cx).eval_local(idx_expr, ctxt) { let span = if name.ident.as_str() == "nth" { e.span } else if let Some((_, Node::Expr(next_expr))) = iter.next() diff --git a/clippy_lints/src/methods/unnecessary_min_or_max.rs b/clippy_lints/src/methods/unnecessary_min_or_max.rs index b87d81b710269..bf91a469e7f01 100644 --- a/clippy_lints/src/methods/unnecessary_min_or_max.rs +++ b/clippy_lints/src/methods/unnecessary_min_or_max.rs @@ -1,7 +1,7 @@ use std::cmp::Ordering; use super::UNNECESSARY_MIN_OR_MAX; -use clippy_utils::consts::{ConstEvalCtxt, Constant, ConstantSource, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; @@ -25,8 +25,9 @@ pub(super) fn check<'tcx>( && let Some(fn_name) = cx.tcx.get_diagnostic_name(id) && matches!(fn_name, sym::cmp_ord_min | sym::cmp_ord_max) { - if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv) - && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg) + let ctxt = expr.span.ctxt(); + if let Some(left) = ecx.eval_local(recv, ctxt) + && let Some(right) = ecx.eval_local(arg, ctxt) { let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else { return; diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index ad9b3c3645425..74b297c13621e 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -1,12 +1,13 @@ -use crate::methods::SelfKind; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_copy; +use itertools::Itertools; use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::{Span, Symbol}; use std::fmt; use super::WRONG_SELF_CONVENTION; +use super::lib::SelfKind; #[rustfmt::skip] const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [ @@ -61,20 +62,20 @@ impl Convention { impl fmt::Display for Convention { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match *self { - Self::Eq(this) => format!("`{this}`").fmt(f), - Self::StartsWith(this) => format!("`{this}*`").fmt(f), - Self::EndsWith(this) => format!("`*{this}`").fmt(f), - Self::NotEndsWith(this) => format!("`~{this}`").fmt(f), + Self::Eq(this) => write!(f, "`{this}`"), + Self::StartsWith(this) => write!(f, "`{this}*`"), + Self::EndsWith(this) => write!(f, "`*{this}`"), + Self::NotEndsWith(this) => write!(f, "`~{this}`"), Self::IsSelfTypeCopy(is_true) => { - format!("`self` type is{} `Copy`", if is_true { "" } else { " not" }).fmt(f) + write!(f, "`self` type is{} `Copy`", if is_true { "" } else { " not" }) }, Self::ImplementsTrait(is_true) => { let (negation, s_suffix) = if is_true { ("", "s") } else { (" does not", "") }; - format!("method{negation} implement{s_suffix} a trait").fmt(f) + write!(f, "method{negation} implement{s_suffix} a trait") }, Self::IsTraitItem(is_true) => { let suffix = if is_true { " is" } else { " is not" }; - format!("method{suffix} a trait item").fmt(f) + write!(f, "method{suffix} a trait item") }, } } @@ -115,18 +116,9 @@ pub(super) fn check<'tcx>( let s = conventions .iter() - .filter_map(|conv| { - if (cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_))) - || matches!(conv, Convention::ImplementsTrait(_)) - || matches!(conv, Convention::IsTraitItem(_)) - { - None - } else { - Some(conv.to_string()) - } - }) - .collect::>() - .join(" and "); + .filter(|conv| !(cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_)))) + .filter(|conv| !matches!(conv, Convention::ImplementsTrait(_) | Convention::IsTraitItem(_))) + .format(" and "); format!("methods with the following characteristics: ({s})") } else { @@ -140,11 +132,7 @@ pub(super) fn check<'tcx>( first_arg_span, format!( "{suggestion} usually take {}", - &self_kinds - .iter() - .map(|k| k.description()) - .collect::>() - .join(" or ") + self_kinds.iter().map(|k| k.description()).format(" or ") ), None, "consider choosing a less ambiguous name", diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 64eafc0ebccdc..f9a7c562c7a59 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -4,6 +4,7 @@ use clippy_utils::{is_trait_method, sym}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::SyntaxContext; use std::cmp::Ordering::{Equal, Greater, Less}; declare_clippy_lint! { @@ -60,7 +61,7 @@ enum MinMax { Max, } -fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant<'tcx>, &'a Expr<'a>)> { +fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> { match expr.kind { ExprKind::Call(path, args) => { if let ExprKind::Path(ref qpath) = path.kind { @@ -68,8 +69,8 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM .qpath_res(qpath, path.hir_id) .opt_def_id() .and_then(|def_id| match cx.tcx.get_diagnostic_name(def_id) { - Some(sym::cmp_min) => fetch_const(cx, None, args, MinMax::Min), - Some(sym::cmp_max) => fetch_const(cx, None, args, MinMax::Max), + Some(sym::cmp_min) => fetch_const(cx, expr.span.ctxt(), None, args, MinMax::Min), + Some(sym::cmp_max) => fetch_const(cx, expr.span.ctxt(), None, args, MinMax::Max), _ => None, }) } else { @@ -79,8 +80,8 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM ExprKind::MethodCall(path, receiver, args @ [_], _) => { if cx.typeck_results().expr_ty(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord) { match path.ident.name { - sym::max => fetch_const(cx, Some(receiver), args, MinMax::Max), - sym::min => fetch_const(cx, Some(receiver), args, MinMax::Min), + sym::max => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Max), + sym::min => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Min), _ => None, } } else { @@ -91,12 +92,13 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM } } -fn fetch_const<'a, 'tcx>( - cx: &LateContext<'tcx>, +fn fetch_const<'a>( + cx: &LateContext<'_>, + ctxt: SyntaxContext, receiver: Option<&'a Expr<'a>>, args: &'a [Expr<'a>], m: MinMax, -) -> Option<(MinMax, Constant<'tcx>, &'a Expr<'a>)> { +) -> Option<(MinMax, Constant, &'a Expr<'a>)> { let mut args = receiver.into_iter().chain(args); let first_arg = args.next()?; let second_arg = args.next()?; @@ -104,7 +106,7 @@ fn fetch_const<'a, 'tcx>( return None; } let ecx = ConstEvalCtxt::new(cx); - match (ecx.eval_simple(first_arg), ecx.eval_simple(second_arg)) { + match (ecx.eval_local(first_arg, ctxt), ecx.eval_local(second_arg, ctxt)) { (Some(c), None) => Some((m, c, second_arg)), (None, Some(c)) => Some((m, c, first_arg)), // otherwise ignore diff --git a/clippy_lints/src/module_style.rs b/clippy_lints/src/module_style.rs index 98614baffcea6..f132b90ac4f2e 100644 --- a/clippy_lints/src/module_style.rs +++ b/clippy_lints/src/module_style.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; -use rustc_ast::ast; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_ast::ast::{self, Inline, ItemKind, ModKind}; use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LOCAL_CRATE; -use rustc_span::{FileName, SourceFile, Span, SyntaxContext}; -use std::ffi::OsStr; -use std::path::{Component, Path}; +use rustc_span::{FileName, SourceFile, Span, SyntaxContext, sym}; +use std::path::{Path, PathBuf}; +use std::sync::Arc; declare_clippy_lint! { /// ### What it does @@ -60,107 +59,97 @@ declare_clippy_lint! { /// mod.rs /// lib.rs /// ``` - #[clippy::version = "1.57.0"] pub SELF_NAMED_MODULE_FILES, restriction, "checks that module layout is consistent" } -pub struct ModStyle; - impl_lint_pass!(ModStyle => [MOD_MODULE_FILES, SELF_NAMED_MODULE_FILES]); +pub struct ModState { + contains_external: bool, + has_path_attr: bool, + mod_file: Arc, +} + +#[derive(Default)] +pub struct ModStyle { + working_dir: Option, + module_stack: Vec, +} + impl EarlyLintPass for ModStyle { fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { + self.working_dir = cx.sess().opts.working_dir.local_path().map(Path::to_path_buf); + } + + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { if cx.builder.lint_level(MOD_MODULE_FILES).level == Level::Allow && cx.builder.lint_level(SELF_NAMED_MODULE_FILES).level == Level::Allow { return; } + if let ItemKind::Mod(.., ModKind::Loaded(_, Inline::No { .. }, mod_spans, ..)) = &item.kind { + let has_path_attr = item.attrs.iter().any(|attr| attr.has_name(sym::path)); + if !has_path_attr && let Some(current) = self.module_stack.last_mut() { + current.contains_external = true; + } + let mod_file = cx.sess().source_map().lookup_source_file(mod_spans.inner_span.lo()); + self.module_stack.push(ModState { + contains_external: false, + has_path_attr, + mod_file, + }); + } + } - let files = cx.sess().source_map().files(); - - let Some(trim_to_src) = cx.sess().opts.working_dir.local_path() else { + fn check_item_post(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { + if cx.builder.lint_level(MOD_MODULE_FILES).level == Level::Allow + && cx.builder.lint_level(SELF_NAMED_MODULE_FILES).level == Level::Allow + { return; - }; - - // `folder_segments` is all unique folder path segments `path/to/foo.rs` gives - // `[path, to]` but not foo - let mut folder_segments = FxIndexSet::default(); - // `mod_folders` is all the unique folder names that contain a mod.rs file - let mut mod_folders = FxHashSet::default(); - // `file_map` maps file names to the full path including the file name - // `{ foo => path/to/foo.rs, .. } - let mut file_map = FxHashMap::default(); - for file in files.iter() { - if let FileName::Real(name) = &file.name - && let Some(lp) = name.local_path() - && file.cnum == LOCAL_CRATE - { - // [#8887](https://github.com/rust-lang/rust-clippy/issues/8887) - // Only check files in the current crate. - // Fix false positive that crate dependency in workspace sub directory - // is checked unintentionally. - let path = if lp.is_relative() { - lp - } else if let Ok(relative) = lp.strip_prefix(trim_to_src) { - relative - } else { - continue; - }; - - if let Some(stem) = path.file_stem() { - file_map.insert(stem, (file, path)); - } - process_paths_for_mod_files(path, &mut folder_segments, &mut mod_folders); - check_self_named_mod_exists(cx, path, file); - } } - for folder in &folder_segments { - if !mod_folders.contains(folder) - && let Some((file, path)) = file_map.get(folder) - { - span_lint_and_then( - cx, - SELF_NAMED_MODULE_FILES, - Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), - format!("`mod.rs` files are required, found `{}`", path.display()), - |diag| { - let mut correct = path.to_path_buf(); - correct.pop(); - correct.push(folder); - correct.push("mod.rs"); - diag.help(format!("move `{}` to `{}`", path.display(), correct.display(),)); - }, - ); + if let ItemKind::Mod(.., ModKind::Loaded(_, Inline::No { .. }, ..)) = &item.kind + && let Some(current) = self.module_stack.pop() + && !current.has_path_attr + { + let Some(path) = self + .working_dir + .as_ref() + .and_then(|src| try_trim_file_path_prefix(¤t.mod_file, src)) + else { + return; + }; + if current.contains_external { + check_self_named_module(cx, path, ¤t.mod_file); } + check_mod_module(cx, path, ¤t.mod_file); } } } -/// For each `path` we add each folder component to `folder_segments` and if the file name -/// is `mod.rs` we add it's parent folder to `mod_folders`. -fn process_paths_for_mod_files<'a>( - path: &'a Path, - folder_segments: &mut FxIndexSet<&'a OsStr>, - mod_folders: &mut FxHashSet<&'a OsStr>, -) { - let mut comp = path.components().rev().peekable(); - let _: Option<_> = comp.next(); - if path.ends_with("mod.rs") { - mod_folders.insert(comp.peek().map(|c| c.as_os_str()).unwrap_or_default()); +fn check_self_named_module(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) { + if !path.ends_with("mod.rs") { + let mut mod_folder = path.with_extension(""); + span_lint_and_then( + cx, + SELF_NAMED_MODULE_FILES, + Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), + format!("`mod.rs` files are required, found `{}`", path.display()), + |diag| { + mod_folder.push("mod.rs"); + diag.help(format!("move `{}` to `{}`", path.display(), mod_folder.display())); + }, + ); } - let folders = comp.filter_map(|c| if let Component::Normal(s) = c { Some(s) } else { None }); - folder_segments.extend(folders); } -/// Checks every path for the presence of `mod.rs` files and emits the lint if found. /// We should not emit a lint for test modules in the presence of `mod.rs`. /// Using `mod.rs` in integration tests is a [common pattern](https://doc.rust-lang.org/book/ch11-03-test-organization.html#submodules-in-integration-test) /// for code-sharing between tests. -fn check_self_named_mod_exists(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) { +fn check_mod_module(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) { if path.ends_with("mod.rs") && !path.starts_with("tests") { span_lint_and_then( cx, @@ -177,3 +166,17 @@ fn check_self_named_mod_exists(cx: &EarlyContext<'_>, path: &Path, file: &Source ); } } + +fn try_trim_file_path_prefix<'a>(file: &'a SourceFile, prefix: &'a Path) -> Option<&'a Path> { + if let FileName::Real(name) = &file.name + && let Some(mut path) = name.local_path() + && file.cnum == LOCAL_CRATE + { + if !path.is_relative() { + path = path.strip_prefix(prefix).ok()?; + } + Some(path) + } else { + None + } +} diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index d98c70e7f5a85..588afd85afb02 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -1,31 +1,51 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_hir}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::higher; -use rustc_hir::{self as hir, AmbigArg, intravisit}; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir::{self as hir, AmbigArg, BorrowKind, Expr, ExprKind, HirId, Mutability, TyKind, intravisit}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; declare_clippy_lint! { /// ### What it does /// Checks for instances of `mut mut` references. /// /// ### Why is this bad? - /// Multiple `mut`s don't add anything meaningful to the - /// source. This is either a copy'n'paste error, or it shows a fundamental - /// misunderstanding of references. + /// This is usually just a typo or a misunderstanding of how references work. /// /// ### Example /// ```no_run - /// # let mut y = 1; - /// let x = &mut &mut y; + /// let x = &mut &mut 1; + /// + /// let mut x = &mut 1; + /// let y = &mut x; + /// + /// fn foo(x: &mut &mut u32) {} + /// ``` + /// Use instead + /// ```no_run + /// let x = &mut 1; + /// + /// let mut x = &mut 1; + /// let y = &mut *x; // reborrow + /// + /// fn foo(x: &mut u32) {} /// ``` #[clippy::version = "pre 1.29.0"] pub MUT_MUT, pedantic, - "usage of double-mut refs, e.g., `&mut &mut ...`" + "usage of double mut-refs, e.g., `&mut &mut ...`" } -declare_lint_pass!(MutMut => [MUT_MUT]); +impl_lint_pass!(MutMut => [MUT_MUT]); + +#[derive(Default)] +pub(crate) struct MutMut { + seen_tys: FxHashSet, +} impl<'tcx> LateLintPass<'tcx> for MutMut { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { @@ -33,17 +53,48 @@ impl<'tcx> LateLintPass<'tcx> for MutMut { } fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'_, AmbigArg>) { - if let hir::TyKind::Ref(_, mty) = ty.kind - && mty.mutbl == hir::Mutability::Mut - && let hir::TyKind::Ref(_, mty) = mty.ty.kind - && mty.mutbl == hir::Mutability::Mut + if let TyKind::Ref(_, mty) = ty.kind + && mty.mutbl == Mutability::Mut + && let TyKind::Ref(_, mty2) = mty.ty.kind + && mty2.mutbl == Mutability::Mut && !ty.span.in_external_macro(cx.sess().source_map()) { - span_lint( + if self.seen_tys.contains(&ty.hir_id) { + // we have 2+ `&mut`s, e.g., `&mut &mut &mut x` + // and we have already flagged on the outermost `&mut &mut (&mut x)`, + // so don't flag the inner `&mut &mut (x)` + return; + } + + // if there is an even longer chain, like `&mut &mut &mut x`, suggest peeling off + // all extra ones at once + let (mut t, mut t2) = (mty.ty, mty2.ty); + let mut many_muts = false; + loop { + // this should allow us to remember all the nested types, so that the `contains` + // above fails faster + self.seen_tys.insert(t.hir_id); + if let TyKind::Ref(_, next) = t2.kind + && next.mutbl == Mutability::Mut + { + (t, t2) = (t2, next.ty); + many_muts = true; + } else { + break; + } + } + + let mut applicability = Applicability::MaybeIncorrect; + let sugg = snippet_with_applicability(cx.sess(), t.span, "..", &mut applicability); + let suffix = if many_muts { "s" } else { "" }; + span_lint_and_sugg( cx, MUT_MUT, ty.span, - "generally you want to avoid `&mut &mut _` if possible", + "a type of form `&mut &mut _`", + format!("remove the extra `&mut`{suffix}"), + sugg.to_string(), + applicability, ); } } @@ -54,7 +105,7 @@ pub struct MutVisitor<'a, 'tcx> { } impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if expr.span.in_external_macro(self.cx.sess().source_map()) { return; } @@ -68,24 +119,60 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> { // Let's ignore the generated code. intravisit::walk_expr(self, arg); 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_hir( + } else if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, e) = expr.kind { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, e2) = e.kind { + if !expr.span.eq_ctxt(e.span) { + return; + } + + // if there is an even longer chain, like `&mut &mut &mut x`, suggest peeling off + // all extra ones at once + let (mut e, mut e2) = (e, e2); + let mut many_muts = false; + loop { + if !e.span.eq_ctxt(e2.span) { + return; + } + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, next) = e2.kind { + (e, e2) = (e2, next); + many_muts = true; + } else { + break; + } + } + + let mut applicability = Applicability::MaybeIncorrect; + let sugg = Sugg::hir_with_applicability(self.cx, e, "..", &mut applicability); + let suffix = if many_muts { "s" } else { "" }; + span_lint_hir_and_then( self.cx, MUT_MUT, expr.hir_id, expr.span, - "generally you want to avoid `&mut &mut _` if possible", + "an expression of form `&mut &mut _`", + |diag| { + diag.span_suggestion( + expr.span, + format!("remove the extra `&mut`{suffix}"), + sugg, + applicability, + ); + }, ); - } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() + } else if let ty::Ref(_, ty, Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() && ty.peel_refs().is_sized(self.cx.tcx, self.cx.typing_env()) { - span_lint_hir( + let mut applicability = Applicability::MaybeIncorrect; + let sugg = Sugg::hir_with_applicability(self.cx, e, "..", &mut applicability).mut_addr_deref(); + span_lint_hir_and_then( self.cx, MUT_MUT, expr.hir_id, expr.span, - "this expression mutably borrows a mutable reference. Consider reborrowing", + "this expression mutably borrows a mutable reference", + |diag| { + diag.span_suggestion(expr.span, "reborrow instead", sugg, applicability); + }, ); } } diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 32ded96c1236f..455d1426aa8cb 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,16 +1,16 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ptr::get_spans; use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{ implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, }; -use clippy_utils::{is_self, peel_hir_ty_options}; +use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; +use clippy_utils::{is_self, path_to_local_id, peel_hir_ty_options, strip_pat_refs, sym}; use rustc_abi::ExternAbi; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - Attribute, BindingMode, Body, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, Node, - PatKind, QPath, TyKind, + Attribute, BindingMode, Body, ExprKind, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, + Node, PatKind, QPath, TyKind, }; use rustc_hir_typeck::expr_use_visitor as euv; use rustc_lint::{LateContext, LateLintPass}; @@ -19,10 +19,13 @@ use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::kw; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol}; use rustc_trait_selection::traits; use rustc_trait_selection::traits::misc::type_allowed_to_implement_copy; +use std::borrow::Cow; +use std::ops::ControlFlow; + declare_clippy_lint! { /// ### What it does /// Checks for functions taking arguments by value, but not @@ -217,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } if is_type_diagnostic_item(cx, ty, sym::Vec) - && let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[(sym::clone, ".to_owned()")]) + && let Some(clone_spans) = get_spans(cx, body, idx, &[(sym::clone, ".to_owned()")]) && let TyKind::Path(QPath::Resolved(_, path)) = input.kind && let Some(elem_ty) = path .segments @@ -260,12 +263,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } if is_type_lang_item(cx, ty, LangItem::String) - && let Some(clone_spans) = get_spans( - cx, - Some(body.id()), - idx, - &[(sym::clone, ".to_string()"), (sym::as_str, "")], - ) + && let Some(clone_spans) = + get_spans(cx, body, idx, &[(sym::clone, ".to_string()"), (sym::as_str, "")]) { diag.span_suggestion( input.span, @@ -340,3 +339,43 @@ impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt { fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } + +fn get_spans<'tcx>( + cx: &LateContext<'tcx>, + body: &'tcx Body<'_>, + idx: usize, + replacements: &[(Symbol, &'static str)], +) -> Option)>> { + if let PatKind::Binding(_, binding_id, _, _) = strip_pat_refs(body.params[idx].pat).kind { + extract_clone_suggestions(cx, binding_id, replacements, body) + } else { + Some(vec![]) + } +} + +fn extract_clone_suggestions<'tcx>( + cx: &LateContext<'tcx>, + id: HirId, + replace: &[(Symbol, &'static str)], + body: &'tcx Body<'_>, +) -> Option)>> { + let mut spans = Vec::new(); + for_each_expr_without_closures(body, |e| { + if let ExprKind::MethodCall(seg, recv, [], _) = e.kind + && path_to_local_id(recv, id) + { + if seg.ident.name == sym::capacity { + return ControlFlow::Break(()); + } + for &(fn_name, suffix) in replace { + if seg.ident.name == fn_name { + spans.push((e.span, snippet(cx, recv.span, "_") + suffix)); + return ControlFlow::Continue(Descend::No); + } + } + } + ControlFlow::Continue(Descend::Yes) + }) + .is_none() + .then_some(spans) +} diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index b598a390005ba..6fc034b6fc5d2 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::return_ty; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::DiagExt; use rustc_errors::Applicability; use rustc_hir as hir; @@ -58,116 +58,132 @@ impl_lint_pass!(NewWithoutDefault => [NEW_WITHOUT_DEFAULT]); impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if let hir::ItemKind::Impl(hir::Impl { + let hir::ItemKind::Impl(hir::Impl { of_trait: None, generics, self_ty: impl_self_ty, .. }) = item.kind + else { + return; + }; + + for assoc_item in cx + .tcx + .associated_items(item.owner_id.def_id) + .filter_by_name_unhygienic(sym::new) { - for assoc_item in cx - .tcx - .associated_items(item.owner_id.def_id) - .filter_by_name_unhygienic(sym::new) + if let AssocKind::Fn { has_self: false, .. } = assoc_item.kind + && let assoc_item_hir_id = cx.tcx.local_def_id_to_hir_id(assoc_item.def_id.expect_local()) + && let impl_item = cx.tcx.hir_node(assoc_item_hir_id).expect_impl_item() + && !impl_item.span.in_external_macro(cx.sess().source_map()) + && let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind + && let id = impl_item.owner_id + // can't be implemented for unsafe new + && !sig.header.is_unsafe() + // shouldn't be implemented when it is hidden in docs + && !cx.tcx.is_doc_hidden(impl_item.owner_id.def_id) + // when the result of `new()` depends on a parameter we should not require + // an impl of `Default` + && impl_item.generics.params.is_empty() + && sig.decl.inputs.is_empty() + && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) + && let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && self_ty == return_ty(cx, impl_item.owner_id) + && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) { - if let AssocKind::Fn { has_self: false, .. } = assoc_item.kind { - let impl_item = cx - .tcx - .hir_node_by_def_id(assoc_item.def_id.expect_local()) - .expect_impl_item(); - if impl_item.span.in_external_macro(cx.sess().source_map()) { - return; - } - if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind { - let id = impl_item.owner_id; - if sig.header.is_unsafe() { - // can't be implemented for unsafe new - return; - } - if cx.tcx.is_doc_hidden(impl_item.owner_id.def_id) { - // shouldn't be implemented when it is hidden in docs - return; - } - if !impl_item.generics.params.is_empty() { - // when the result of `new()` depends on a parameter we should not require - // an impl of `Default` - return; - } - if sig.decl.inputs.is_empty() - && cx.effective_visibilities.is_reachable(impl_item.owner_id.def_id) - && let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity() - && self_ty == return_ty(cx, impl_item.owner_id) - && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) + if self.impling_types.is_none() { + let mut impls = HirIdSet::default(); + for &d in cx.tcx.local_trait_impls(default_trait_id) { + let ty = cx.tcx.type_of(d).instantiate_identity(); + if let Some(ty_def) = ty.ty_adt_def() + && let Some(local_def_id) = ty_def.did().as_local() { - if self.impling_types.is_none() { - let mut impls = HirIdSet::default(); - for &d in cx.tcx.local_trait_impls(default_trait_id) { - let ty = cx.tcx.type_of(d).instantiate_identity(); - if let Some(ty_def) = ty.ty_adt_def() - && let Some(local_def_id) = ty_def.did().as_local() - { - impls.insert(cx.tcx.local_def_id_to_hir_id(local_def_id)); - } - } - self.impling_types = Some(impls); - } + impls.insert(cx.tcx.local_def_id_to_hir_id(local_def_id)); + } + } + self.impling_types = Some(impls); + } - // Check if a Default implementation exists for the Self type, regardless of - // generics - if let Some(ref impling_types) = self.impling_types - && let self_def = cx.tcx.type_of(item.owner_id).instantiate_identity() - && let Some(self_def) = self_def.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) - && impling_types.contains(&self_id) - { - return; - } + // Check if a Default implementation exists for the Self type, regardless of + // generics + if let Some(ref impling_types) = self.impling_types + && let self_def = cx.tcx.type_of(item.owner_id).instantiate_identity() + && let Some(self_def) = self_def.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) + && impling_types.contains(&self_id) + { + return; + } - let generics_sugg = snippet(cx, generics.span, ""); - let where_clause_sugg = if generics.has_where_clause_predicates { - format!("\n{}\n", snippet(cx, generics.where_clause_span, "")) - } else { - String::new() - }; - let self_ty_fmt = self_ty.to_string(); - let self_type_snip = snippet(cx, impl_self_ty.span, &self_ty_fmt); - span_lint_hir_and_then( - cx, - NEW_WITHOUT_DEFAULT, - id.into(), - impl_item.span, - format!("you should consider adding a `Default` implementation for `{self_type_snip}`"), - |diag| { - diag.suggest_prepend_item( - cx, - item.span, - "try adding this", - &create_new_without_default_suggest_msg( - &self_type_snip, - &generics_sugg, - &where_clause_sugg, - ), - Applicability::MachineApplicable, - ); - }, - ); + let mut app = Applicability::MachineApplicable; + let attrs_sugg = { + let mut sugg = String::new(); + for attr in cx.tcx.hir_attrs(assoc_item_hir_id) { + if !attr.has_name(sym::cfg_trace) { + // This might be some other attribute that the `impl Default` ought to inherit. + // But it could also be one of the many attributes that: + // - can't be put on an impl block -- like `#[inline]` + // - we can't even build a suggestion for, since `Attribute::span` may panic. + // + // Because of all that, remain on the safer side -- don't inherit this attr, and just + // reduce the applicability + app = Applicability::MaybeIncorrect; + continue; } + + sugg.push_str(&snippet_with_applicability(cx.sess(), attr.span(), "_", &mut app)); + sugg.push('\n'); } - } + sugg + }; + let generics_sugg = snippet_with_applicability(cx, generics.span, "", &mut app); + let where_clause_sugg = if generics.has_where_clause_predicates { + format!( + "\n{}\n", + snippet_with_applicability(cx, generics.where_clause_span, "", &mut app) + ) + } else { + String::new() + }; + let self_ty_fmt = self_ty.to_string(); + let self_type_snip = snippet_with_applicability(cx, impl_self_ty.span, &self_ty_fmt, &mut app); + span_lint_hir_and_then( + cx, + NEW_WITHOUT_DEFAULT, + id.into(), + impl_item.span, + format!("you should consider adding a `Default` implementation for `{self_type_snip}`"), + |diag| { + diag.suggest_prepend_item( + cx, + item.span, + "try adding this", + &create_new_without_default_suggest_msg( + &attrs_sugg, + &self_type_snip, + &generics_sugg, + &where_clause_sugg, + ), + app, + ); + }, + ); } } } } fn create_new_without_default_suggest_msg( + attrs_sugg: &str, self_type_snip: &str, generics_sugg: &str, where_clause_sugg: &str, ) -> String { #[rustfmt::skip] format!( -"impl{generics_sugg} Default for {self_type_snip}{where_clause_sugg} {{ +"{attrs_sugg}impl{generics_sugg} Default for {self_type_snip}{where_clause_sugg} {{ fn default() -> Self {{ Self::new() }} diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index ba67dc62abbda..e531f797272d2 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -5,9 +5,9 @@ use clippy_utils::{ }; use rustc_errors::Applicability; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, LangItem, Node, UnOp}; +use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, LangItem, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::EarlyBinder; +use rustc_middle::ty::{EarlyBinder, TraitRef}; use rustc_session::declare_lint_pass; use rustc_span::sym; use rustc_span::symbol::kw; @@ -112,140 +112,146 @@ declare_clippy_lint! { declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL_PARTIAL_ORD_IMPL]); impl LateLintPass<'_> for NonCanonicalImpls { - #[expect(clippy::too_many_lines)] fn check_impl_item<'tcx>(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'tcx>) { - let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) else { - return; - }; - let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) else { - return; - }; - if cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) { - return; + if let ImplItemKind::Fn(_, impl_item_id) = impl_item.kind + && let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) + && let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) + && let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id) + // NOTE: check this early to avoid expensive checks that come after this one + && matches!(trait_name, Some(sym::Clone | sym::PartialOrd)) + && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) + && let body = cx.tcx.hir_body(impl_item_id) + && let ExprKind::Block(block, ..) = body.value.kind + && !block.span.in_external_macro(cx.sess().source_map()) + && !is_from_proc_macro(cx, impl_item) + { + if trait_name == Some(sym::Clone) + && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) + && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[]) + { + check_clone_on_copy(cx, impl_item, block); + } else if trait_name == Some(sym::PartialOrd) + && impl_item.ident.name == sym::partial_cmp + && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) + && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) + { + check_partial_ord_on_ord(cx, impl_item, item, &trait_impl, body, block); + } } - let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir_impl_item(impl_item.impl_item_id()).kind else { - return; - }; - let body = cx.tcx.hir_body(impl_item_id); - let ExprKind::Block(block, ..) = body.value.kind else { - return; - }; - if block.span.in_external_macro(cx.sess().source_map()) || is_from_proc_macro(cx, impl_item) { + } +} + +fn check_clone_on_copy(cx: &LateContext<'_>, impl_item: &ImplItem<'_>, block: &Block<'_>) { + if impl_item.ident.name == sym::clone { + if block.stmts.is_empty() + && let Some(expr) = block.expr + && let ExprKind::Unary(UnOp::Deref, deref) = expr.kind + && let ExprKind::Path(qpath) = deref.kind + && last_path_segment(&qpath).ident.name == kw::SelfLower + { + // this is the canonical implementation, `fn clone(&self) -> Self { *self }` return; } - let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id); - if trait_name == Some(sym::Clone) - && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) - && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[]) - { - if impl_item.ident.name == sym::clone { - if block.stmts.is_empty() - && let Some(expr) = block.expr - && let ExprKind::Unary(UnOp::Deref, deref) = expr.kind - && let ExprKind::Path(qpath) = deref.kind - && last_path_segment(&qpath).ident.name == kw::SelfLower - { - } else { - span_lint_and_sugg( - cx, - NON_CANONICAL_CLONE_IMPL, - block.span, - "non-canonical implementation of `clone` on a `Copy` type", - "change this to", - "{ *self }".to_owned(), - Applicability::MaybeIncorrect, - ); + span_lint_and_sugg( + cx, + NON_CANONICAL_CLONE_IMPL, + block.span, + "non-canonical implementation of `clone` on a `Copy` type", + "change this to", + "{ *self }".to_owned(), + Applicability::MaybeIncorrect, + ); + } - return; - } - } + if impl_item.ident.name == sym::clone_from { + span_lint_and_sugg( + cx, + NON_CANONICAL_CLONE_IMPL, + impl_item.span, + "unnecessary implementation of `clone_from` on a `Copy` type", + "remove it", + String::new(), + Applicability::MaybeIncorrect, + ); + } +} - if impl_item.ident.name == sym::clone_from { - span_lint_and_sugg( - cx, - NON_CANONICAL_CLONE_IMPL, - impl_item.span, - "unnecessary implementation of `clone_from` on a `Copy` type", - "remove it", - String::new(), - Applicability::MaybeIncorrect, - ); - } - } else if trait_name == Some(sym::PartialOrd) - && impl_item.ident.name == sym::partial_cmp - && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) - && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) - { - // If the `cmp` call likely needs to be fully qualified in the suggestion - // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't - // access `cmp_expr` in the suggestion without major changes, as we lint in `else`. - let mut needs_fully_qualified = false; +fn check_partial_ord_on_ord<'tcx>( + cx: &LateContext<'tcx>, + impl_item: &ImplItem<'_>, + item: &Item<'_>, + trait_impl: &TraitRef<'_>, + body: &Body<'_>, + block: &Block<'tcx>, +) { + // If the `cmp` call likely needs to be fully qualified in the suggestion + // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't + // access `cmp_expr` in the suggestion without major changes, as we lint in `else`. - if block.stmts.is_empty() - && let Some(expr) = block.expr - && expr_is_cmp(cx, expr, impl_item, &mut needs_fully_qualified) - { - return; - } - // Fix #12683, allow [`needless_return`] here - else if block.expr.is_none() - && let Some(stmt) = block.stmts.first() - && let rustc_hir::StmtKind::Semi(Expr { - kind: ExprKind::Ret(Some(ret)), - .. - }) = stmt.kind - && expr_is_cmp(cx, ret, impl_item, &mut needs_fully_qualified) - { + let mut needs_fully_qualified = false; + if block.stmts.is_empty() + && let Some(expr) = block.expr + && expr_is_cmp(cx, expr, impl_item, &mut needs_fully_qualified) + { + return; + } + // Fix #12683, allow [`needless_return`] here + else if block.expr.is_none() + && let Some(stmt) = block.stmts.first() + && let rustc_hir::StmtKind::Semi(Expr { + kind: ExprKind::Ret(Some(ret)), + .. + }) = stmt.kind + && expr_is_cmp(cx, ret, impl_item, &mut needs_fully_qualified) + { + return; + } + // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid + // suggestion tons more complex. + else if let [lhs, rhs, ..] = trait_impl.args.as_slice() + && lhs != rhs + { + return; + } + + span_lint_and_then( + cx, + NON_CANONICAL_PARTIAL_ORD_IMPL, + item.span, + "non-canonical implementation of `partial_cmp` on an `Ord` type", + |diag| { + let [_, other] = body.params else { return; - } - // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid - // suggestion tons more complex. - else if let [lhs, rhs, ..] = trait_impl.args.as_slice() - && lhs != rhs - { + }; + let Some(std_or_core) = std_or_core(cx) else { return; - } - - span_lint_and_then( - cx, - NON_CANONICAL_PARTIAL_ORD_IMPL, - item.span, - "non-canonical implementation of `partial_cmp` on an `Ord` type", - |diag| { - let [_, other] = body.params else { - return; - }; - let Some(std_or_core) = std_or_core(cx) else { - return; - }; - - let suggs = match (other.pat.simple_ident(), needs_fully_qualified) { - (Some(other_ident), true) => vec![( - block.span, - format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}", other_ident.name), - )], - (Some(other_ident), false) => { - vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] - }, - (None, true) => vec![ - ( - block.span, - format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}"), - ), - (other.pat.span, "other".to_owned()), - ], - (None, false) => vec![ - (block.span, "{ Some(self.cmp(other)) }".to_owned()), - (other.pat.span, "other".to_owned()), - ], - }; + }; - diag.multipart_suggestion("change this to", suggs, Applicability::Unspecified); + let suggs = match (other.pat.simple_ident(), needs_fully_qualified) { + (Some(other_ident), true) => vec![( + block.span, + format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}", other_ident.name), + )], + (Some(other_ident), false) => { + vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))] }, - ); - } - } + (None, true) => vec![ + ( + block.span, + format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}"), + ), + (other.pat.span, "other".to_owned()), + ], + (None, false) => vec![ + (block.span, "{ Some(self.cmp(other)) }".to_owned()), + (other.pat.span, "other".to_owned()), + ], + }; + + diag.multipart_suggestion("change this to", suggs, Applicability::Unspecified); + }, + ); } /// Return true if `expr_kind` is a `cmp` call. @@ -256,26 +262,27 @@ fn expr_is_cmp<'tcx>( needs_fully_qualified: &mut bool, ) -> bool { let impl_item_did = impl_item.owner_id.def_id; - if let ExprKind::Call( - Expr { - kind: ExprKind::Path(some_path), - hir_id: some_hir_id, - .. - }, - [cmp_expr], - ) = expr.kind - { - is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) + match expr.kind { + ExprKind::Call( + Expr { + kind: ExprKind::Path(some_path), + hir_id: some_hir_id, + .. + }, + [cmp_expr], + ) => { + is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) // Fix #11178, allow `Self::cmp(self, ..)` too && self_cmp_call(cx, cmp_expr, impl_item_did, needs_fully_qualified) - } else if let ExprKind::MethodCall(_, recv, [], _) = expr.kind { - cx.tcx - .typeck(impl_item_did) - .type_dependent_def_id(expr.hir_id) - .is_some_and(|def_id| is_diag_trait_item(cx, def_id, sym::Into)) - && self_cmp_call(cx, recv, impl_item_did, needs_fully_qualified) - } else { - false + }, + ExprKind::MethodCall(_, recv, [], _) => { + cx.tcx + .typeck(impl_item_did) + .type_dependent_def_id(expr.hir_id) + .is_some_and(|def_id| is_diag_trait_item(cx, def_id, sym::Into)) + && self_cmp_call(cx, recv, impl_item_did, needs_fully_qualified) + }, + _ => false, } } diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index 83f7d9319697b..3a8a4dd0c7137 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -16,8 +16,8 @@ declare_clippy_lint! { /// Checks that common macros are used with consistent bracing. /// /// ### Why is this bad? - /// This is mostly a consistency lint although using () or [] - /// doesn't give you a semicolon in item position, which can be unexpected. + /// Having non-conventional braces on well-stablished macros can be confusing + /// when debugging, and they bring incosistencies with the rest of the ecosystem. /// /// ### Example /// ```no_run @@ -33,8 +33,12 @@ declare_clippy_lint! { "check consistent use of braces in macro" } -/// The (callsite span, (open brace, close brace), source snippet) -type MacroInfo = (Span, (char, char), SourceText); +struct MacroInfo { + callsite_span: Span, + callsite_snippet: SourceText, + old_open_brace: char, + braces: (char, char), +} pub struct MacroBraces { macro_braces: FxHashMap, @@ -54,30 +58,58 @@ impl_lint_pass!(MacroBraces => [NONSTANDARD_MACRO_BRACES]); impl EarlyLintPass for MacroBraces { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if let Some((span, braces, snip)) = is_offending_macro(cx, item.span, self) { - emit_help(cx, &snip, braces, span); - self.done.insert(span); + if let Some(MacroInfo { + callsite_span, + callsite_snippet, + braces, + .. + }) = is_offending_macro(cx, item.span, self) + { + emit_help(cx, &callsite_snippet, braces, callsite_span, false); + self.done.insert(callsite_span); } } fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) { - if let Some((span, braces, snip)) = is_offending_macro(cx, stmt.span, self) { - emit_help(cx, &snip, braces, span); - self.done.insert(span); + if let Some(MacroInfo { + callsite_span, + callsite_snippet, + braces, + old_open_brace, + }) = is_offending_macro(cx, stmt.span, self) + { + // if we turn `macro!{}` into `macro!()`/`macro![]`, we'll no longer get the implicit + // trailing semicolon, see #9913 + // NOTE: `stmt.kind != StmtKind::MacCall` because `EarlyLintPass` happens after macro expansion + let add_semi = matches!(stmt.kind, ast::StmtKind::Expr(..)) && old_open_brace == '{'; + emit_help(cx, &callsite_snippet, braces, callsite_span, add_semi); + self.done.insert(callsite_span); } } fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - if let Some((span, braces, snip)) = is_offending_macro(cx, expr.span, self) { - emit_help(cx, &snip, braces, span); - self.done.insert(span); + if let Some(MacroInfo { + callsite_span, + callsite_snippet, + braces, + .. + }) = is_offending_macro(cx, expr.span, self) + { + emit_help(cx, &callsite_snippet, braces, callsite_span, false); + self.done.insert(callsite_span); } } fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { - if let Some((span, braces, snip)) = is_offending_macro(cx, ty.span, self) { - emit_help(cx, &snip, braces, span); - self.done.insert(span); + if let Some(MacroInfo { + callsite_span, + braces, + callsite_snippet, + .. + }) = is_offending_macro(cx, ty.span, self) + { + emit_help(cx, &callsite_snippet, braces, callsite_span, false); + self.done.insert(callsite_span); } } } @@ -90,39 +122,44 @@ fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBrace .last() .is_some_and(|e| e.macro_def_id.is_some_and(DefId::is_local)) }; - let span_call_site = span.ctxt().outer_expn_data().call_site; + let callsite_span = span.ctxt().outer_expn_data().call_site; if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind && let name = mac_name.as_str() && let Some(&braces) = mac_braces.macro_braces.get(name) - && let Some(snip) = span_call_site.get_source_text(cx) + && let Some(snip) = callsite_span.get_source_text(cx) // we must check only invocation sites // https://github.com/rust-lang/rust-clippy/issues/7422 - && snip.starts_with(&format!("{name}!")) + && let Some(macro_args_str) = snip.strip_prefix(name).and_then(|snip| snip.strip_prefix('!')) + && let Some(old_open_brace @ ('{' | '(' | '[')) = macro_args_str.trim_start().chars().next() + && old_open_brace != braces.0 && unnested_or_local() - // make formatting consistent - && let c = snip.replace(' ', "") - && !c.starts_with(&format!("{name}!{}", braces.0)) - && !mac_braces.done.contains(&span_call_site) + && !mac_braces.done.contains(&callsite_span) { - Some((span_call_site, braces, snip)) + Some(MacroInfo { + callsite_span, + callsite_snippet: snip, + old_open_brace, + braces, + }) } else { None } } -fn emit_help(cx: &EarlyContext<'_>, snip: &str, (open, close): (char, char), span: Span) { +fn emit_help(cx: &EarlyContext<'_>, snip: &str, (open, close): (char, char), span: Span, add_semi: bool) { + let semi = if add_semi { ";" } else { "" }; if let Some((macro_name, macro_args_str)) = snip.split_once('!') { let mut macro_args = macro_args_str.trim().to_string(); // now remove the wrong braces - macro_args.remove(0); macro_args.pop(); + macro_args.remove(0); span_lint_and_sugg( cx, NONSTANDARD_MACRO_BRACES, span, format!("use of irregular braces for `{macro_name}!` macro"), "consider writing", - format!("{macro_name}!{open}{macro_args}{close}"), + format!("{macro_name}!{open}{macro_args}{close}{semi}"), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index a21c361356e83..ec8c2299d8cbb 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -24,6 +24,33 @@ declare_clippy_lint! { /// the calculations have no side-effects (function calls or mutating dereference) /// and the assigned variables are also only in recursion, it is useless. /// + /// ### Example + /// ```no_run + /// fn f(a: usize, b: usize) -> usize { + /// if a == 0 { + /// 1 + /// } else { + /// f(a - 1, b + 1) + /// } + /// } + /// # fn main() { + /// # print!("{}", f(1, 1)); + /// # } + /// ``` + /// Use instead: + /// ```no_run + /// fn f(a: usize) -> usize { + /// if a == 0 { + /// 1 + /// } else { + /// f(a - 1) + /// } + /// } + /// # fn main() { + /// # print!("{}", f(1)); + /// # } + /// ``` + /// /// ### Known problems /// Too many code paths in the linting code are currently untested and prone to produce false /// positives or are prone to have performance implications. @@ -51,39 +78,90 @@ declare_clippy_lint! { /// - struct pattern binding /// /// Also, when you recurse the function name with path segments, it is not possible to detect. + #[clippy::version = "1.61.0"] + pub ONLY_USED_IN_RECURSION, + complexity, + "arguments that is only used in recursion can be removed" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `self` receiver that is only used in recursion with no side-effects. + /// + /// ### Why is this bad? + /// + /// It may be possible to remove the `self` argument, allowing the function to be + /// used without an object of type `Self`. /// /// ### Example /// ```no_run - /// fn f(a: usize, b: usize) -> usize { - /// if a == 0 { - /// 1 - /// } else { - /// f(a - 1, b + 1) + /// struct Foo; + /// impl Foo { + /// fn f(&self, n: u32) -> u32 { + /// if n == 0 { + /// 1 + /// } else { + /// n * self.f(n - 1) + /// } /// } /// } /// # fn main() { - /// # print!("{}", f(1, 1)); + /// # print!("{}", Foo.f(10)); /// # } /// ``` /// Use instead: /// ```no_run - /// fn f(a: usize) -> usize { - /// if a == 0 { - /// 1 - /// } else { - /// f(a - 1) + /// struct Foo; + /// impl Foo { + /// fn f(n: u32) -> u32 { + /// if n == 0 { + /// 1 + /// } else { + /// n * Self::f(n - 1) + /// } /// } /// } /// # fn main() { - /// # print!("{}", f(1)); + /// # print!("{}", Foo::f(10)); /// # } /// ``` - #[clippy::version = "1.61.0"] - pub ONLY_USED_IN_RECURSION, - complexity, - "arguments that is only used in recursion can be removed" + /// + /// ### Known problems + /// Too many code paths in the linting code are currently untested and prone to produce false + /// positives or are prone to have performance implications. + /// + /// In some cases, this would not catch all useless arguments. + /// + /// ```no_run + /// struct Foo; + /// impl Foo { + /// fn foo(&self, a: usize) -> usize { + /// let f = |x| x; + /// + /// if a == 0 { + /// 1 + /// } else { + /// f(self).foo(a) + /// } + /// } + /// } + /// ``` + /// + /// For example, here `self` is only used in recursion, but the lint would not catch it. + /// + /// List of some examples that can not be caught: + /// - binary operation of non-primitive types + /// - closure usage + /// - some `break` relative operations + /// - struct pattern binding + /// + /// Also, when you recurse the function name with path segments, it is not possible to detect. + #[clippy::version = "1.92.0"] + pub SELF_ONLY_USED_IN_RECURSION, + pedantic, + "self receiver only used to recursively call method can be removed" } -impl_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]); +impl_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION, SELF_ONLY_USED_IN_RECURSION]); #[derive(Clone, Copy)] enum FnKind { @@ -357,26 +435,39 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { self.params.flag_for_linting(); for param in &self.params.params { if param.apply_lint.get() { - span_lint_and_then( - cx, - ONLY_USED_IN_RECURSION, - param.ident.span, - "parameter is only used in recursion", - |diag| { - if param.ident.name != kw::SelfLower { + if param.ident.name == kw::SelfLower { + span_lint_and_then( + cx, + SELF_ONLY_USED_IN_RECURSION, + param.ident.span, + "`self` is only used in recursion", + |diag| { + diag.span_note( + param.uses.iter().map(|x| x.span).collect::>(), + "`self` used here", + ); + }, + ); + } else { + span_lint_and_then( + cx, + ONLY_USED_IN_RECURSION, + param.ident.span, + "parameter is only used in recursion", + |diag| { diag.span_suggestion( param.ident.span, "if this is intentional, prefix it with an underscore", format!("_{}", param.ident.name), Applicability::MaybeIncorrect, ); - } - diag.span_note( - param.uses.iter().map(|x| x.span).collect::>(), - "parameter used here", - ); - }, - ); + diag.span_note( + param.uses.iter().map(|x| x.span).collect::>(), + "parameter used here", + ); + }, + ); + } } } self.params.clear(); diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index ea5b81aec31ea..e062e55dad894 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -190,7 +190,7 @@ impl ArithmeticSideEffects { lhs: &'tcx hir::Expr<'_>, rhs: &'tcx hir::Expr<'_>, ) { - if ConstEvalCtxt::new(cx).eval_simple(expr).is_some() { + if ConstEvalCtxt::new(cx).eval_local(expr, expr.span.ctxt()).is_some() { return; } if !matches!( @@ -283,7 +283,7 @@ impl ArithmeticSideEffects { let Some(arg) = args.first() else { return; }; - if ConstEvalCtxt::new(cx).eval_simple(receiver).is_some() { + if ConstEvalCtxt::new(cx).eval_local(receiver, expr.span.ctxt()).is_some() { return; } let instance_ty = cx.typeck_results().expr_ty_adjusted(receiver); diff --git a/clippy_lints/src/operators/const_comparisons.rs b/clippy_lints/src/operators/const_comparisons.rs index 10455d3b93a00..56001a185771a 100644 --- a/clippy_lints/src/operators/const_comparisons.rs +++ b/clippy_lints/src/operators/const_comparisons.rs @@ -22,7 +22,7 @@ fn comparison_to_const<'tcx>( cx: &LateContext<'tcx>, typeck: &'tcx TypeckResults<'tcx>, expr: &'tcx Expr<'tcx>, -) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant<'tcx>, Ty<'tcx>)> { +) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant, Ty<'tcx>)> { if let ExprKind::Binary(operator, left, right) = expr.kind && let Ok(cmp_op) = CmpOp::try_from(operator.node) { diff --git a/clippy_lints/src/operators/duration_subsec.rs b/clippy_lints/src/operators/duration_subsec.rs index 6c9be7c5e90b3..d897b0e8dd918 100644 --- a/clippy_lints/src/operators/duration_subsec.rs +++ b/clippy_lints/src/operators/duration_subsec.rs @@ -19,7 +19,7 @@ pub(crate) fn check<'tcx>( if op == BinOpKind::Div && let ExprKind::MethodCall(method_path, self_arg, [], _) = left.kind && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration) - && let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval(right) + && let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval_local(right, expr.span.ctxt()) { let suggested_fn = match (method_path.ident.name, divisor) { (sym::subsec_micros, 1_000) | (sym::subsec_nanos, 1_000_000) => "subsec_millis", diff --git a/clippy_lints/src/operators/erasing_op.rs b/clippy_lints/src/operators/erasing_op.rs index 8f5ee390f7222..ae1a94a3e23f0 100644 --- a/clippy_lints/src/operators/erasing_op.rs +++ b/clippy_lints/src/operators/erasing_op.rs @@ -39,7 +39,9 @@ fn check_op<'tcx>( other: &Expr<'tcx>, parent: &Expr<'tcx>, ) { - if ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), tck).eval_simple(op) == Some(Constant::Int(0)) { + if ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), tck).eval_local(op, parent.span.ctxt()) + == Some(Constant::Int(0)) + { if different_types(tck, other, parent) { return; } diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs index ded161c8576a1..eb2353cfd90bf 100644 --- a/clippy_lints/src/operators/float_cmp.rs +++ b/clippy_lints/src/operators/float_cmp.rs @@ -18,12 +18,13 @@ pub(crate) fn check<'tcx>( ) { if (op == BinOpKind::Eq || op == BinOpKind::Ne) && is_float(cx, left) { let ecx = ConstEvalCtxt::new(cx); - let left_is_local = match ecx.eval_with_source(left) { + let ctxt = expr.span.ctxt(); + let left_is_local = match ecx.eval_with_source(left, ctxt) { Some((c, s)) if !is_allowed(&c) => s.is_local(), Some(_) => return, None => true, }; - let right_is_local = match ecx.eval_with_source(right) { + let right_is_local = match ecx.eval_with_source(right, ctxt) { Some((c, s)) if !is_allowed(&c) => s.is_local(), Some(_) => return, None => true, @@ -84,7 +85,7 @@ fn get_lint_and_message(is_local: bool, is_comparing_arrays: bool) -> (&'static } } -fn is_allowed(val: &Constant<'_>) -> bool { +fn is_allowed(val: &Constant) -> bool { match val { // FIXME(f16_f128): add when equality check is available on all platforms &Constant::F32(f) => f == 0.0 || f.is_infinite(), diff --git a/clippy_lints/src/operators/identity_op.rs b/clippy_lints/src/operators/identity_op.rs index 3efbb8963587f..43c62e1e131a3 100644 --- a/clippy_lints/src/operators/identity_op.rs +++ b/clippy_lints/src/operators/identity_op.rs @@ -7,7 +7,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, Path, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{Span, kw}; +use rustc_span::{Span, SyntaxContext, kw}; use super::IDENTITY_OP; @@ -41,42 +41,43 @@ pub(crate) fn check<'tcx>( (span, is_coerced) }; + let ctxt = expr.span.ctxt(); match op { BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { - if is_redundant_op(cx, left, 0) { + if is_redundant_op(cx, left, 0, ctxt) { let paren = needs_parenthesis(cx, expr, right); span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value); - } else if is_redundant_op(cx, right, 0) { + } else if is_redundant_op(cx, right, 0, ctxt) { let paren = needs_parenthesis(cx, expr, left); span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } }, BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { - if is_redundant_op(cx, right, 0) { + if is_redundant_op(cx, right, 0, ctxt) { let paren = needs_parenthesis(cx, expr, left); span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } }, BinOpKind::Mul => { - if is_redundant_op(cx, left, 1) { + if is_redundant_op(cx, left, 1, ctxt) { let paren = needs_parenthesis(cx, expr, right); span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value); - } else if is_redundant_op(cx, right, 1) { + } else if is_redundant_op(cx, right, 1, ctxt) { let paren = needs_parenthesis(cx, expr, left); span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } }, BinOpKind::Div => { - if is_redundant_op(cx, right, 1) { + if is_redundant_op(cx, right, 1, ctxt) { let paren = needs_parenthesis(cx, expr, left); span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } }, BinOpKind::BitAnd => { - if is_redundant_op(cx, left, -1) { + if is_redundant_op(cx, left, -1, ctxt) { let paren = needs_parenthesis(cx, expr, right); span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value); - } else if is_redundant_op(cx, right, -1) { + } else if is_redundant_op(cx, right, -1, ctxt) { let paren = needs_parenthesis(cx, expr, left); span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } @@ -184,14 +185,17 @@ fn is_allowed<'tcx>( // This lint applies to integers and their references cx.typeck_results().expr_ty(left).peel_refs().is_integral() - && cx.typeck_results().expr_ty(right).peel_refs().is_integral() + && cx.typeck_results().expr_ty(right).peel_refs().is_integral() // `1 << 0` is a common pattern in bit manipulation code - && !(cmp == BinOpKind::Shl && is_zero_integer_const(cx, right) && integer_const(cx, left) == Some(1)) + && !(cmp == BinOpKind::Shl + && is_zero_integer_const(cx, right, expr.span.ctxt()) + && integer_const(cx, left, expr.span.ctxt()) == Some(1)) } fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) { let ecx = ConstEvalCtxt::new(cx); - if match (ecx.eval_full_int(left), ecx.eval_full_int(right)) { + let ctxt = span.ctxt(); + if match (ecx.eval_full_int(left, ctxt), ecx.eval_full_int(right, ctxt)) { (Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv.abs(), (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv, _ => return, @@ -200,8 +204,8 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span } } -fn is_redundant_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8) -> bool { - if let Some(Constant::Int(v)) = ConstEvalCtxt::new(cx).eval_simple(e).map(Constant::peel_refs) { +fn is_redundant_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, ctxt: SyntaxContext) -> bool { + if let Some(Constant::Int(v)) = ConstEvalCtxt::new(cx).eval_local(e, ctxt).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), diff --git a/clippy_lints/src/operators/manual_is_multiple_of.rs b/clippy_lints/src/operators/manual_is_multiple_of.rs index 55bb78cfce5fc..0b9bd4fb6d320 100644 --- a/clippy_lints/src/operators/manual_is_multiple_of.rs +++ b/clippy_lints/src/operators/manual_is_multiple_of.rs @@ -13,14 +13,14 @@ use super::MANUAL_IS_MULTIPLE_OF; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, - expr: &Expr<'_>, + expr: &'tcx Expr<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>, msrv: Msrv, ) { if msrv.meets(cx, msrvs::UNSIGNED_IS_MULTIPLE_OF) - && let Some(operand) = uint_compare_to_zero(cx, op, lhs, rhs) + && let Some(operand) = uint_compare_to_zero(cx, expr, op, lhs, rhs) && let ExprKind::Binary(operand_op, operand_left, operand_right) = operand.kind && operand_op.node == BinOpKind::Rem && matches!( @@ -57,18 +57,19 @@ pub(super) fn check<'tcx>( // If we have a `x == 0`, `x != 0` or `x > 0` (or the reverted ones), return the non-zero operand fn uint_compare_to_zero<'tcx>( cx: &LateContext<'tcx>, + e: &'tcx Expr<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>, ) -> Option<&'tcx Expr<'tcx>> { let operand = if matches!(lhs.kind, ExprKind::Binary(..)) && matches!(op, BinOpKind::Eq | BinOpKind::Ne | BinOpKind::Gt) - && is_zero_integer_const(cx, rhs) + && is_zero_integer_const(cx, rhs, e.span.ctxt()) { lhs } else if matches!(rhs.kind, ExprKind::Binary(..)) && matches!(op, BinOpKind::Eq | BinOpKind::Ne | BinOpKind::Lt) - && is_zero_integer_const(cx, lhs) + && is_zero_integer_const(cx, lhs, e.span.ctxt()) { rhs } else { diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index bdbbb3475cd5f..aaea4ff11fc37 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -854,7 +854,7 @@ declare_clippy_lint! { /// println!("{a} is divisible by {b}"); /// } /// ``` - #[clippy::version = "1.89.0"] + #[clippy::version = "1.90.0"] pub MANUAL_IS_MULTIPLE_OF, complexity, "manual implementation of `.is_multiple_of()`" diff --git a/clippy_lints/src/operators/modulo_arithmetic.rs b/clippy_lints/src/operators/modulo_arithmetic.rs index b79461663d7bf..ffe91fc2cef6d 100644 --- a/clippy_lints/src/operators/modulo_arithmetic.rs +++ b/clippy_lints/src/operators/modulo_arithmetic.rs @@ -39,7 +39,9 @@ fn used_in_comparison_with_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { && let BinOpKind::Eq | BinOpKind::Ne = op.node { let ecx = ConstEvalCtxt::new(cx); - matches!(ecx.eval(lhs), Some(Constant::Int(0))) || matches!(ecx.eval(rhs), Some(Constant::Int(0))) + let ctxt = expr.span.ctxt(); + matches!(ecx.eval_local(lhs, ctxt), Some(Constant::Int(0))) + || matches!(ecx.eval_local(rhs, ctxt), Some(Constant::Int(0))) } else { false } @@ -55,7 +57,7 @@ fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> match ConstEvalCtxt::new(cx).eval(operand)? { Constant::Int(v) => match *cx.typeck_results().expr_ty(expr).kind() { ty::Int(ity) => { - let value = sext(cx.tcx, v, ity); + let value: i128 = sext(cx.tcx, v, ity); Some(OperandInfo { string_representation: Some(value.to_string()), is_negative: value < 0, diff --git a/clippy_lints/src/operators/numeric_arithmetic.rs b/clippy_lints/src/operators/numeric_arithmetic.rs index 9b1b063c4737d..622f328f369a5 100644 --- a/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/clippy_lints/src/operators/numeric_arithmetic.rs @@ -55,7 +55,7 @@ impl Context { return; } let ty = cx.typeck_results().expr_ty(arg); - if ConstEvalCtxt::new(cx).eval_simple(expr).is_none() && ty.is_floating_point() { + if ConstEvalCtxt::new(cx).eval_local(expr, expr.span.ctxt()).is_none() && ty.is_floating_point() { span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); self.expr_id = Some(expr.hir_id); } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 4aa100a50e053..d3a5a5dddfbeb 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -6,7 +6,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::usage::local_used_after_expr; use clippy_utils::{ eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_path_lang_item, is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, @@ -483,6 +484,13 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: .filter(|e| *e) .is_none() { + if !is_copy(cx, caller_ty) + && let Some(hir_id) = path_to_local(let_expr) + && local_used_after_expr(cx, hir_id, expr) + { + return; + } + 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(_)); diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 03d00ba849f3c..0b2313cb7eeb9 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -299,8 +299,8 @@ fn check_possible_range_contains( } } -struct RangeBounds<'a, 'tcx> { - val: Constant<'tcx>, +struct RangeBounds<'a> { + val: Constant, expr: &'a Expr<'a>, id: HirId, name_span: Span, @@ -312,7 +312,7 @@ struct RangeBounds<'a, 'tcx> { // Takes a binary expression such as x <= 2 as input // Breaks apart into various pieces, such as the value of the number, // hir id of the variable, and direction/inclusiveness of the operator -fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> Option> { +fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option> { if let ExprKind::Binary(ref op, l, r) = ex.kind { let (inclusive, ordering) = match op.node { BinOpKind::Gt => (false, Ordering::Greater), diff --git a/clippy_lints/src/time_subtraction.rs b/clippy_lints/src/time_subtraction.rs new file mode 100644 index 0000000000000..fde8c3d9a9a76 --- /dev/null +++ b/clippy_lints/src/time_subtraction.rs @@ -0,0 +1,216 @@ +use clippy_config::Conf; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; +use clippy_utils::{is_path_diagnostic_item, ty}; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; +use rustc_session::impl_lint_pass; +use rustc_span::source_map::Spanned; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Lints subtraction between `Instant::now()` and another `Instant`. + /// + /// ### Why is this bad? + /// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns + /// as `Instant` subtraction saturates. + /// + /// `prev_instant.elapsed()` also more clearly signals intention. + /// + /// ### Example + /// ```no_run + /// use std::time::Instant; + /// let prev_instant = Instant::now(); + /// let duration = Instant::now() - prev_instant; + /// ``` + /// Use instead: + /// ```no_run + /// use std::time::Instant; + /// let prev_instant = Instant::now(); + /// let duration = prev_instant.elapsed(); + /// ``` + #[clippy::version = "1.65.0"] + pub MANUAL_INSTANT_ELAPSED, + pedantic, + "subtraction between `Instant::now()` and previous `Instant`" +} + +declare_clippy_lint! { + /// ### What it does + /// Lints subtraction between an `Instant` and a `Duration`, or between two `Duration` values. + /// + /// ### Why is this bad? + /// Unchecked subtraction could cause underflow on certain platforms, leading to + /// unintentional panics. + /// + /// ### Example + /// ```no_run + /// # use std::time::{Instant, Duration}; + /// let time_passed = Instant::now() - Duration::from_secs(5); + /// let dur1 = Duration::from_secs(3); + /// let dur2 = Duration::from_secs(5); + /// let diff = dur1 - dur2; + /// ``` + /// + /// Use instead: + /// ```no_run + /// # use std::time::{Instant, Duration}; + /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); + /// let dur1 = Duration::from_secs(3); + /// let dur2 = Duration::from_secs(5); + /// let diff = dur1.checked_sub(dur2); + /// ``` + #[clippy::version = "1.67.0"] + pub UNCHECKED_TIME_SUBTRACTION, + pedantic, + "finds unchecked subtraction involving 'Duration' or 'Instant'" +} + +pub struct UncheckedTimeSubtraction { + msrv: Msrv, +} + +impl UncheckedTimeSubtraction { + pub fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv } + } +} + +impl_lint_pass!(UncheckedTimeSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_TIME_SUBTRACTION]); + +impl LateLintPass<'_> for UncheckedTimeSubtraction { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, .. + }, + lhs, + rhs, + ) = expr.kind + { + let typeck = cx.typeck_results(); + let lhs_ty = typeck.expr_ty(lhs); + let rhs_ty = typeck.expr_ty(rhs); + + if ty::is_type_diagnostic_item(cx, lhs_ty, sym::Instant) { + // Instant::now() - instant + if is_instant_now_call(cx, lhs) + && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Instant) + && let Some(sugg) = Sugg::hir_opt(cx, rhs) + { + print_manual_instant_elapsed_sugg(cx, expr, sugg); + } + // instant - duration + else if ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) + && !expr.span.from_expansion() + && self.msrv.meets(cx, msrvs::TRY_FROM) + { + // For chained subtraction like (instant - dur1) - dur2, avoid suggestions + if is_chained_time_subtraction(cx, lhs) { + span_lint( + cx, + UNCHECKED_TIME_SUBTRACTION, + expr.span, + "unchecked subtraction of a 'Duration' from an 'Instant'", + ); + } else { + // instant - duration + print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); + } + } + } else if ty::is_type_diagnostic_item(cx, lhs_ty, sym::Duration) + && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) + && !expr.span.from_expansion() + && self.msrv.meets(cx, msrvs::TRY_FROM) + { + // For chained subtraction like (dur1 - dur2) - dur3, avoid suggestions + if is_chained_time_subtraction(cx, lhs) { + span_lint( + cx, + UNCHECKED_TIME_SUBTRACTION, + expr.span, + "unchecked subtraction between 'Duration' values", + ); + } else { + // duration - duration + print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); + } + } + } + } +} + +fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { + if let ExprKind::Call(fn_expr, []) = expr_block.kind + && is_path_diagnostic_item(cx, fn_expr, sym::instant_now) + { + true + } else { + false + } +} + +/// Returns true if this subtraction is part of a chain like `(a - b) - c` +fn is_chained_time_subtraction(cx: &LateContext<'_>, lhs: &Expr<'_>) -> bool { + if let ExprKind::Binary(op, inner_lhs, inner_rhs) = &lhs.kind + && matches!(op.node, BinOpKind::Sub) + { + let typeck = cx.typeck_results(); + let left_ty = typeck.expr_ty(inner_lhs); + let right_ty = typeck.expr_ty(inner_rhs); + is_time_type(cx, left_ty) && is_time_type(cx, right_ty) + } else { + false + } +} + +/// Returns true if the type is Duration or Instant +fn is_time_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + ty::is_type_diagnostic_item(cx, ty, sym::Duration) || ty::is_type_diagnostic_item(cx, ty, sym::Instant) +} + +fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) { + span_lint_and_sugg( + cx, + MANUAL_INSTANT_ELAPSED, + expr.span, + "manual implementation of `Instant::elapsed`", + "try", + format!("{}.elapsed()", sugg.maybe_paren()), + Applicability::MachineApplicable, + ); +} + +fn print_unchecked_duration_subtraction_sugg( + cx: &LateContext<'_>, + left_expr: &Expr<'_>, + right_expr: &Expr<'_>, + expr: &Expr<'_>, +) { + let typeck = cx.typeck_results(); + let left_ty = typeck.expr_ty(left_expr); + + let lint_msg = if ty::is_type_diagnostic_item(cx, left_ty, sym::Instant) { + "unchecked subtraction of a 'Duration' from an 'Instant'" + } else { + "unchecked subtraction between 'Duration' values" + }; + + let mut applicability = Applicability::MachineApplicable; + let left_sugg = Sugg::hir_with_applicability(cx, left_expr, "", &mut applicability); + let right_sugg = Sugg::hir_with_applicability(cx, right_expr, "", &mut applicability); + + span_lint_and_sugg( + cx, + UNCHECKED_TIME_SUBTRACTION, + expr.span, + lint_msg, + "try", + format!("{}.checked_sub({}).unwrap()", left_sugg.maybe_paren(), right_sugg), + applicability, + ); +} diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index d5b6c17585494..2645e94358e11 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, is_format_macro, root_macro_call_first_node}; -use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_context}; +use clippy_utils::source::{snippet_indent, walk_span_to_context}; use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source}; use core::ops::ControlFlow; use rustc_ast::{FormatArgs, FormatArgumentKind}; @@ -74,10 +74,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, format_args: &FormatArgsStorag "this let-binding has unit value", |diag| { let mut suggestions = Vec::new(); + let init_new_span = walk_span_to_context(init.span, local.span.ctxt()).unwrap(); // Suggest omitting the `let` binding - let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, init.span, local.span.ctxt(), "()", &mut app).0; + let app = Applicability::MachineApplicable; // If this is a binding pattern, we need to add suggestions to remove any usages // of the variable @@ -89,35 +89,49 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, format_args: &FormatArgsStorag walk_body(&mut visitor, body); let mut has_in_format_capture = false; - suggestions.extend(visitor.spans.iter().filter_map(|span| match span { - MaybeInFormatCapture::Yes => { + suggestions.extend(visitor.spans.into_iter().filter_map(|span| match span { + VariableUsage::FormatCapture => { has_in_format_capture = true; None }, - MaybeInFormatCapture::No(span) => Some((*span, "()".to_string())), + VariableUsage::Normal(span) => Some((span, "()".to_string())), + VariableUsage::FieldShorthand(span) => Some((span.shrink_to_hi(), ": ()".to_string())), })); if has_in_format_capture { + // In a case like this: + // ``` + // let unit = returns_unit(); + // eprintln!("{unit}"); + // ``` + // we can't remove the `unit` binding and replace its uses with a `()`, + // because the `eprintln!` would break. + // + // So do the following instead: + // ``` + // let unit = (); + // returns_unit(); + // eprintln!("{unit}"); + // ``` + // TODO: find a less awkward way to do this suggestions.push(( - init.span, - format!("();\n{}", reindent_multiline(&snip, false, indent_of(cx, local.span))), + init_new_span.shrink_to_lo(), + format!("();\n{}", snippet_indent(cx, local.span).as_deref().unwrap_or("")), )); - diag.multipart_suggestion( - "replace variable usages with `()`", - suggestions, - Applicability::MachineApplicable, - ); + diag.multipart_suggestion_verbose("replace variable usages with `()`", suggestions, app); return; } } - suggestions.push((local.span, format!("{snip};"))); + // let local = returns_unit(); + // ^^^^^^^^^^^^ remove this + suggestions.push((local.span.until(init_new_span), String::new())); let message = if suggestions.len() == 1 { "omit the `let` binding" } else { "omit the `let` binding and replace variable usages with `()`" }; - diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable); + diag.multipart_suggestion_verbose(message, suggestions, app); }, ); } @@ -128,13 +142,30 @@ struct UnitVariableCollector<'a, 'tcx> { cx: &'a LateContext<'tcx>, format_args: &'a FormatArgsStorage, id: HirId, - spans: Vec, + spans: Vec, macro_call: Option<&'a FormatArgs>, } -enum MaybeInFormatCapture { - Yes, - No(Span), +/// How the unit variable is used +enum VariableUsage { + Normal(Span), + /// Captured in a `format!`: + /// + /// ```ignore + /// let unit = (); + /// eprintln!("{unit}"); + /// ``` + FormatCapture, + /// In a field shorthand init: + /// + /// ```ignore + /// struct Foo { + /// unit: (), + /// } + /// let unit = (); + /// Foo { unit }; + /// ``` + FieldShorthand(Span), } impl<'a, 'tcx> UnitVariableCollector<'a, 'tcx> { @@ -174,9 +205,17 @@ impl<'tcx> Visitor<'tcx> for UnitVariableCollector<'_, 'tcx> { matches!(arg.kind, FormatArgumentKind::Captured(_)) && find_format_arg_expr(ex, arg).is_some() }) { - self.spans.push(MaybeInFormatCapture::Yes); + self.spans.push(VariableUsage::FormatCapture); } else { - self.spans.push(MaybeInFormatCapture::No(path.span)); + let parent = self.cx.tcx.parent_hir_node(ex.hir_id); + match parent { + Node::ExprField(expr_field) if expr_field.is_shorthand => { + self.spans.push(VariableUsage::FieldShorthand(ex.span)); + }, + _ => { + self.spans.push(VariableUsage::Normal(path.span)); + }, + } } } diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/unnecessary_mut_passed.rs similarity index 72% rename from clippy_lints/src/mut_reference.rs rename to clippy_lints/src/unnecessary_mut_passed.rs index ec93ef97cfaf7..eb2d7639e91f7 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/unnecessary_mut_passed.rs @@ -1,5 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::sugg::Sugg; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; @@ -87,16 +87,33 @@ fn check_arguments<'tcx>( if let ty::Ref(_, _, Mutability::Not) | ty::RawPtr(_, Mutability::Not) = parameter.kind() && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, arg) = argument.kind { - let mut applicability = Applicability::MachineApplicable; - let sugg = Sugg::hir_with_applicability(cx, arg, "_", &mut applicability).addr(); - span_lint_and_sugg( + let applicability = Applicability::MachineApplicable; + + let span_to_remove = { + let span_until_arg = argument.span.until(arg.span); + if let Some(Some(ref_pos)) = span_until_arg.with_source_text(cx, |src| { + src + // we don't use `strip_prefix` here, because `argument` might be enclosed in parens, in + // which case `&` is no longer the prefix + .find('&') + // just a sanity check, in case some proc-macro messes up the spans + .filter(|ref_pos| src[*ref_pos..].contains("mut")) + }) && let Ok(lo) = u32::try_from(ref_pos + '&'.len_utf8()) + { + span_until_arg.split_at(lo).1 + } else { + return; + } + }; + + span_lint_and_then( cx, UNNECESSARY_MUT_PASSED, argument.span, format!("the {fn_kind} `{name}` doesn't need a mutable reference"), - "remove this `mut`", - sugg.to_string(), - applicability, + |diag| { + diag.span_suggestion_verbose(span_to_remove, "remove this `mut`", String::new(), applicability); + }, ); } } diff --git a/clippy_lints/src/unnecessary_semicolon.rs b/clippy_lints/src/unnecessary_semicolon.rs index 76e24b6bf8058..e1e450a52fdf0 100644 --- a/clippy_lints/src/unnecessary_semicolon.rs +++ b/clippy_lints/src/unnecessary_semicolon.rs @@ -88,7 +88,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessarySemicolon { ) && cx.typeck_results().expr_ty(expr).is_unit() // if a stmt has attrs, then turning it into an expr will break the code, since attrs aren't allowed on exprs - && cx.tcx.hir_attrs(stmt.hir_id).is_empty() + // -- unless the corresponding feature is enabled + && (cx.tcx.hir_attrs(stmt.hir_id).is_empty() || cx.tcx.features().stmt_expr_attributes()) { if let Some(block_is_unit) = self.is_last_in_block(stmt) { if cx.tcx.sess.edition() <= Edition2021 && leaks_droppable_temporary_with_limited_lifetime(cx, expr) { diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 34dfe5b6546fa..aee8028a75de7 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,7 +1,9 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::msrvs::Msrv; +use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::usage::is_potentially_local_place; -use clippy_utils::{higher, path_to_local, sym}; +use clippy_utils::{can_use_if_let_chains, higher, path_to_local, sym}; use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, UnOp}; @@ -10,7 +12,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::{Span, Symbol}; @@ -72,10 +74,21 @@ declare_clippy_lint! { "checks for calls of `unwrap[_err]()` that will always fail" } +pub(crate) struct Unwrap { + msrv: Msrv, +} + +impl Unwrap { + pub fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv } + } +} + /// Visitor that keeps track of which variables are unwrappable. struct UnwrappableVariablesVisitor<'a, 'tcx> { unwrappables: Vec>, cx: &'a LateContext<'tcx>, + msrv: Msrv, } /// What kind of unwrappable this is. @@ -133,12 +146,14 @@ fn collect_unwrap_info<'tcx>( invert: bool, is_entire_condition: bool, ) -> Vec> { - fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { - is_type_diagnostic_item(cx, ty, sym::Option) && matches!(method_name, sym::is_none | sym::is_some) - } - - fn is_relevant_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { - is_type_diagnostic_item(cx, ty, sym::Result) && matches!(method_name, sym::is_err | sym::is_ok) + fn option_or_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<(UnwrappableKind, bool)> { + match (get_type_diagnostic_name(cx, ty)?, method_name) { + (sym::Option, sym::is_some) => Some((UnwrappableKind::Option, true)), + (sym::Option, sym::is_none) => Some((UnwrappableKind::Option, false)), + (sym::Result, sym::is_ok) => Some((UnwrappableKind::Result, true)), + (sym::Result, sym::is_err) => Some((UnwrappableKind::Result, false)), + _ => None, + } } match expr.kind { @@ -157,15 +172,9 @@ fn collect_unwrap_info<'tcx>( if let Some(local_id) = path_to_local(receiver) && let ty = cx.typeck_results().expr_ty(receiver) && let name = method_name.ident.name - && (is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name)) => + && let Some((kind, unwrappable)) = option_or_result_call(cx, ty, name) => { - let unwrappable = matches!(name, sym::is_some | sym::is_ok); let safe_to_unwrap = unwrappable != invert; - let kind = if is_type_diagnostic_item(cx, ty, sym::Option) { - UnwrappableKind::Option - } else { - UnwrappableKind::Result - }; vec![UnwrapInfo { local_id, @@ -357,7 +366,11 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { ); } else { diag.span_label(unwrappable.check.span, "the check is happening here"); - diag.help("try using `if let` or `match`"); + if can_use_if_let_chains(self.cx, self.msrv) { + diag.help("try using `if let` or `match`"); + } else { + diag.help("try using `match`"); + } } }, ); @@ -383,7 +396,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { } } -declare_lint_pass!(Unwrap => [PANICKING_UNWRAP, UNNECESSARY_UNWRAP]); +impl_lint_pass!(Unwrap => [PANICKING_UNWRAP, UNNECESSARY_UNWRAP]); impl<'tcx> LateLintPass<'tcx> for Unwrap { fn check_fn( @@ -402,6 +415,7 @@ impl<'tcx> LateLintPass<'tcx> for Unwrap { let mut v = UnwrappableVariablesVisitor { unwrappables: Vec::new(), cx, + msrv: self.msrv, }; walk_fn(&mut v, kind, decl, body.id(), fn_id); diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index 5eb207a0aedb0..bb0cab3a30757 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -37,8 +37,9 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv { // That's probably fine for this lint - it's pretty unlikely that someone would // do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too. && let ecx = ConstEvalCtxt::new(cx) - && let Some(lhs_value) = ecx.eval_simple(left) - && let Some(rhs_value) = ecx.eval_simple(right) + && let ctxt = expr.span.ctxt() + && let Some(lhs_value) = ecx.eval_local(left, ctxt) + && let Some(rhs_value) = ecx.eval_local(right, ctxt) // FIXME(f16_f128): add these types when eq is available on all platforms && (Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value) && (Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value) diff --git a/clippy_lints/src/zero_repeat_side_effects.rs b/clippy_lints/src/zero_repeat_side_effects.rs index 30fdf22fdbb09..cd6c11b512742 100644 --- a/clippy_lints/src/zero_repeat_side_effects.rs +++ b/clippy_lints/src/zero_repeat_side_effects.rs @@ -1,19 +1,16 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet; -use clippy_utils::visitors::for_each_expr_without_closures; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{ConstArgKind, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::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, + /// Checks for array or vec initializations which contain an expression with side effects, /// but which have a repeat count of zero. /// /// ### Why is this bad? @@ -73,89 +70,43 @@ impl LateLintPass<'_> for ZeroRepeatSideEffects { 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_without_closures(inner_expr, |x| { - if let ExprKind::Call(_, _) | ExprKind::MethodCall(_, _, _, _) = x.kind { - std::ops::ControlFlow::Break(()) - } else { - std::ops::ControlFlow::Continue(()) - } - }) - .is_some() - { + if inner_expr.can_have_side_effects() { let parent_hir_node = cx.tcx.parent_hir_node(expr.hir_id); let return_type = cx.typeck_results().expr_ty(expr); - if let Node::LetStmt(l) = parent_hir_node { - array_span_lint( - cx, + let inner_expr = snippet(cx, inner_expr.span.source_callsite(), ".."); + let vec = if is_vec { "vec!" } else { "" }; + + let (span, sugg) = match parent_hir_node { + Node::LetStmt(l) => ( 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 { "" }, + "{inner_expr}; let {var_name}: {return_type} = {vec}[];", + var_name = snippet(cx, l.pat.span.source_callsite(), "..") ), - 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() + ), + Node::Expr(x) if let ExprKind::Assign(l, _, _) = x.kind => ( + x.span, + format!( + "{inner_expr}; {var_name} = {vec}[] as {return_type}", + var_name = snippet(cx, l.span.source_callsite(), "..") + ), + ), + _ => (expr.span, format!("{{ {inner_expr}; {vec}[] as {return_type} }}")), + }; + span_lint_and_then( + cx, + ZERO_REPEAT_SIDE_EFFECTS, + span.source_callsite(), + "expression with side effects as the initial value in a zero-sized array initializer", + |diag| { + diag.span_suggestion_verbose( + span.source_callsite(), + "consider performing the side effect separately", + sugg, + Applicability::Unspecified, + ); }, - if is_assign { "" } else { ";" } - ), - Applicability::Unspecified, - ); + ); + } } diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 2c66fdc73f539..1f678a6a29f08 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-09-18 +nightly-2025-10-06 ``` diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index ecd88daa6b39f..9ba796137cc3a 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -5,25 +5,21 @@ #![allow(clippy::float_cmp)] use crate::source::{SpanRangeExt, walk_span_to_context}; -use crate::{clip, is_direct_expn_of, sext, unsext}; +use crate::{clip, is_direct_expn_of, paths, sext, sym, unsext}; use rustc_abi::Size; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Half, Quad}; use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{ - BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, PatExpr, PatExprKind, QPath, UnOp, -}; +use rustc_hir::{BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, PatExpr, PatExprKind, QPath, TyKind, UnOp}; use rustc_lexer::{FrontmatterAllowed, tokenize}; use rustc_lint::LateContext; use rustc_middle::mir::ConstValue; use rustc_middle::mir::interpret::{Scalar, alloc_range}; use rustc_middle::ty::{self, FloatTy, IntTy, ScalarInt, Ty, TyCtxt, TypeckResults, UintTy}; use rustc_middle::{bug, mir, span_bug}; -use rustc_span::def_id::DefId; -use rustc_span::symbol::Ident; -use rustc_span::{SyntaxContext, sym}; +use rustc_span::{Symbol, SyntaxContext}; use std::cell::Cell; use std::cmp::Ordering; use std::hash::{Hash, Hasher}; @@ -31,8 +27,8 @@ use std::iter; /// A `LitKind`-like enum to fold constant `Expr`s into. #[derive(Debug, Clone)] -pub enum Constant<'tcx> { - Adt(mir::Const<'tcx>), +pub enum Constant { + Adt(ConstValue), /// A `String` (e.g., "abc"). Str(String), /// A binary string (e.g., `b"abc"`). @@ -54,15 +50,15 @@ pub enum Constant<'tcx> { /// `true` or `false`. Bool(bool), /// An array of constants. - Vec(Vec>), + Vec(Vec), /// Also an array, but with only one constant, repeated N times. - Repeat(Box>, u64), + Repeat(Box, u64), /// A tuple of constants. - Tuple(Vec>), + Tuple(Vec), /// A raw pointer. RawPtr(u128), /// A reference - Ref(Box>), + Ref(Box), /// A literal with syntax error. Err, } @@ -124,7 +120,7 @@ impl IntTypeBounds for IntTy { } } -impl PartialEq for Constant<'_> { +impl PartialEq for Constant { fn eq(&self, other: &Self) -> bool { match (self, other) { (Self::Str(ls), Self::Str(rs)) => ls == rs, @@ -132,16 +128,12 @@ impl PartialEq for Constant<'_> { (&Self::Char(l), &Self::Char(r)) => l == r, (&Self::Int(l), &Self::Int(r)) => l == r, (&Self::F64(l), &Self::F64(r)) => { - // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have - // `Fw32 == Fw64`, so don’t compare them. - // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. - l.to_bits() == r.to_bits() + // `to_bits` is required to catch non-matching `0.0` and `-0.0`. + l.to_bits() == r.to_bits() && !l.is_nan() }, (&Self::F32(l), &Self::F32(r)) => { - // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have - // `Fw32 == Fw64`, so don’t compare them. - // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. - f64::from(l).to_bits() == f64::from(r).to_bits() + // `to_bits` is required to catch non-matching `0.0` and `-0.0`. + l.to_bits() == r.to_bits() && !l.is_nan() }, (&Self::Bool(l), &Self::Bool(r)) => l == r, (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r, @@ -153,7 +145,7 @@ impl PartialEq for Constant<'_> { } } -impl Hash for Constant<'_> { +impl Hash for Constant { fn hash(&self, state: &mut H) where H: Hasher, @@ -209,7 +201,7 @@ impl Hash for Constant<'_> { } } -impl Constant<'_> { +impl Constant { pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option { match (left, right) { (Self::Str(ls), Self::Str(rs)) => Some(ls.cmp(rs)), @@ -297,10 +289,129 @@ impl Constant<'_> { let f: Quad = s.parse().unwrap(); Self::F128(f.to_bits()) } + + pub fn new_numeric_min<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { + match *ty.kind() { + ty::Uint(_) => Some(Self::Int(0)), + ty::Int(ty) => { + let val = match ty.normalize(tcx.sess.target.pointer_width) { + IntTy::I8 => i128::from(i8::MIN), + IntTy::I16 => i128::from(i16::MIN), + IntTy::I32 => i128::from(i32::MIN), + IntTy::I64 => i128::from(i64::MIN), + IntTy::I128 => i128::MIN, + IntTy::Isize => return None, + }; + Some(Self::Int(val.cast_unsigned())) + }, + ty::Char => Some(Self::Char(char::MIN)), + ty::Float(FloatTy::F32) => Some(Self::F32(f32::NEG_INFINITY)), + ty::Float(FloatTy::F64) => Some(Self::F64(f64::NEG_INFINITY)), + _ => None, + } + } + + pub fn new_numeric_max<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { + match *ty.kind() { + ty::Uint(ty) => Some(Self::Int(match ty.normalize(tcx.sess.target.pointer_width) { + UintTy::U8 => u128::from(u8::MAX), + UintTy::U16 => u128::from(u16::MAX), + UintTy::U32 => u128::from(u32::MAX), + UintTy::U64 => u128::from(u64::MAX), + UintTy::U128 => u128::MAX, + UintTy::Usize => return None, + })), + ty::Int(ty) => { + let val = match ty.normalize(tcx.sess.target.pointer_width) { + IntTy::I8 => i128::from(i8::MAX), + IntTy::I16 => i128::from(i16::MAX), + IntTy::I32 => i128::from(i32::MAX), + IntTy::I64 => i128::from(i64::MAX), + IntTy::I128 => i128::MAX, + IntTy::Isize => return None, + }; + Some(Self::Int(val.cast_unsigned())) + }, + ty::Char => Some(Self::Char(char::MAX)), + ty::Float(FloatTy::F32) => Some(Self::F32(f32::INFINITY)), + ty::Float(FloatTy::F64) => Some(Self::F64(f64::INFINITY)), + _ => None, + } + } + + pub fn is_numeric_min<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match (self, ty.kind()) { + (&Self::Int(x), &ty::Uint(_)) => x == 0, + (&Self::Int(x), &ty::Int(ty)) => { + let limit = match ty.normalize(tcx.sess.target.pointer_width) { + IntTy::I8 => i128::from(i8::MIN), + IntTy::I16 => i128::from(i16::MIN), + IntTy::I32 => i128::from(i32::MIN), + IntTy::I64 => i128::from(i64::MIN), + IntTy::I128 => i128::MIN, + IntTy::Isize => return false, + }; + x.cast_signed() == limit + }, + (&Self::Char(x), &ty::Char) => x == char::MIN, + (&Self::F32(x), &ty::Float(FloatTy::F32)) => x == f32::NEG_INFINITY, + (&Self::F64(x), &ty::Float(FloatTy::F64)) => x == f64::NEG_INFINITY, + _ => false, + } + } + + pub fn is_numeric_max<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match (self, ty.kind()) { + (&Self::Int(x), &ty::Uint(ty)) => { + let limit = match ty.normalize(tcx.sess.target.pointer_width) { + UintTy::U8 => u128::from(u8::MAX), + UintTy::U16 => u128::from(u16::MAX), + UintTy::U32 => u128::from(u32::MAX), + UintTy::U64 => u128::from(u64::MAX), + UintTy::U128 => u128::MAX, + UintTy::Usize => return false, + }; + x == limit + }, + (&Self::Int(x), &ty::Int(ty)) => { + let limit = match ty.normalize(tcx.sess.target.pointer_width) { + IntTy::I8 => i128::from(i8::MAX), + IntTy::I16 => i128::from(i16::MAX), + IntTy::I32 => i128::from(i32::MAX), + IntTy::I64 => i128::from(i64::MAX), + IntTy::I128 => i128::MAX, + IntTy::Isize => return false, + }; + x.cast_signed() == limit + }, + (&Self::Char(x), &ty::Char) => x == char::MAX, + (&Self::F32(x), &ty::Float(FloatTy::F32)) => x == f32::INFINITY, + (&Self::F64(x), &ty::Float(FloatTy::F64)) => x == f64::INFINITY, + _ => false, + } + } + + pub fn is_pos_infinity(&self) -> bool { + match *self { + // FIXME(f16_f128): add f16 and f128 when constants are available + Constant::F32(x) => x == f32::INFINITY, + Constant::F64(x) => x == f64::INFINITY, + _ => false, + } + } + + pub fn is_neg_infinity(&self) -> bool { + match *self { + // FIXME(f16_f128): add f16 and f128 when constants are available + Constant::F32(x) => x == f32::NEG_INFINITY, + Constant::F64(x) => x == f64::NEG_INFINITY, + _ => false, + } + } } /// Parses a `LitKind` to a `Constant`. -pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option>) -> Constant<'tcx> { +pub fn lit_to_mir_constant(lit: &LitKind, ty: Option>) -> Constant { match *lit { LitKind::Str(ref is, _) => Constant::Str(is.to_string()), LitKind::Byte(b) => Constant::Int(u128::from(b)), @@ -331,10 +442,9 @@ pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option>) -> Constan pub enum ConstantSource { /// The value is determined solely from the expression. Local, - /// The value is dependent on a defined constant. - Constant, - /// The value is dependent on a constant defined in `core` crate. - CoreConstant, + /// The value is dependent on another definition that may change independently from the local + /// expression. + NonLocal, } impl ConstantSource { pub fn is_local(self) -> bool { @@ -387,6 +497,7 @@ pub struct ConstEvalCtxt<'tcx> { typing_env: ty::TypingEnv<'tcx>, typeck: &'tcx TypeckResults<'tcx>, source: Cell, + ctxt: Cell, } impl<'tcx> ConstEvalCtxt<'tcx> { @@ -398,6 +509,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { typing_env: cx.typing_env(), typeck: cx.typeck_results(), source: Cell::new(ConstantSource::Local), + ctxt: Cell::new(SyntaxContext::root()), } } @@ -408,38 +520,50 @@ impl<'tcx> ConstEvalCtxt<'tcx> { typing_env, typeck, source: Cell::new(ConstantSource::Local), + ctxt: Cell::new(SyntaxContext::root()), } } /// Attempts to evaluate the expression and returns both the value and whether it's dependant on /// other items. - pub fn eval_with_source(&self, e: &Expr<'_>) -> Option<(Constant<'tcx>, ConstantSource)> { + pub fn eval_with_source(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option<(Constant, ConstantSource)> { self.source.set(ConstantSource::Local); + self.ctxt.set(ctxt); self.expr(e).map(|c| (c, self.source.get())) } /// Attempts to evaluate the expression. - pub fn eval(&self, e: &Expr<'_>) -> Option> { + pub fn eval(&self, e: &Expr<'_>) -> Option { self.expr(e) } /// Attempts to evaluate the expression without accessing other items. - pub fn eval_simple(&self, e: &Expr<'_>) -> Option> { - match self.eval_with_source(e) { + /// + /// The context argument is the context used to view the evaluated expression. e.g. when + /// evaluating the argument in `f(m!(1))` the context of the call expression should be used. + /// This is need so the const evaluator can see the `m` macro and marke the evaluation as + /// non-local independant of what the macro expands to. + pub fn eval_local(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option { + match self.eval_with_source(e, ctxt) { Some((x, ConstantSource::Local)) => Some(x), _ => None, } } /// Attempts to evaluate the expression as an integer without accessing other items. - pub fn eval_full_int(&self, e: &Expr<'_>) -> Option { - match self.eval_with_source(e) { + /// + /// The context argument is the context used to view the evaluated expression. e.g. when + /// evaluating the argument in `f(m!(1))` the context of the call expression should be used. + /// This is need so the const evaluator can see the `m` macro and marke the evaluation as + /// non-local independant of what the macro expands to. + pub fn eval_full_int(&self, e: &Expr<'_>, ctxt: SyntaxContext) -> Option { + match self.eval_with_source(e, ctxt) { Some((x, ConstantSource::Local)) => x.int_value(self.tcx, self.typeck.expr_ty(e)), _ => None, } } - pub fn eval_pat_expr(&self, pat_expr: &PatExpr<'_>) -> Option> { + pub fn eval_pat_expr(&self, pat_expr: &PatExpr<'_>) -> Option { match &pat_expr.kind { PatExprKind::Lit { lit, negated } => { let ty = self.typeck.node_type_opt(pat_expr.hir_id); @@ -455,39 +579,31 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } - fn qpath(&self, qpath: &QPath<'_>, hir_id: HirId) -> Option> { - let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, hir_id).opt_def_id() { - self.tcx.crate_name(def_id.krate) == sym::core - } else { - false - }; - self.fetch_path_and_apply(qpath, hir_id, self.typeck.node_type(hir_id), |self_, result| { - let result = mir_to_const(self_.tcx, result)?; - // If source is already Constant we wouldn't want to override it with CoreConstant - self_.source.set( - if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) { - ConstantSource::CoreConstant - } else { - ConstantSource::Constant - }, - ); - Some(result) - }) + fn check_ctxt(&self, ctxt: SyntaxContext) { + if self.ctxt.get() != ctxt { + self.source.set(ConstantSource::NonLocal); + } + } + + fn qpath(&self, qpath: &QPath<'_>, hir_id: HirId) -> Option { + self.fetch_path(qpath, hir_id) + .and_then(|c| mir_to_const(self.tcx, c, self.typeck.node_type(hir_id))) } /// Simple constant folding: Insert an expression, get a constant or none. - fn expr(&self, e: &Expr<'_>) -> Option> { + fn expr(&self, e: &Expr<'_>) -> Option { + self.check_ctxt(e.span.ctxt()); match e.kind { ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir_body(body).value), ExprKind::DropTemps(e) => self.expr(e), ExprKind::Path(ref qpath) => self.qpath(qpath, e.hir_id), - ExprKind::Block(block, _) => self.block(block), + ExprKind::Block(block, _) => { + self.check_ctxt(block.span.ctxt()); + self.block(block) + }, ExprKind::Lit(lit) => { - if is_direct_expn_of(e.span, sym::cfg).is_some() { - None - } else { - Some(lit_to_mir_constant(&lit.node, self.typeck.expr_ty_opt(e))) - } + self.check_ctxt(lit.span.ctxt()); + Some(lit_to_mir_constant(&lit.node, self.typeck.expr_ty_opt(e))) }, ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec), ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple), @@ -504,7 +620,10 @@ impl<'tcx> ConstEvalCtxt<'tcx> { UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }), }), ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise), - ExprKind::Binary(op, left, right) => self.binop(op.node, left, right), + ExprKind::Binary(op, left, right) => { + self.check_ctxt(e.span.ctxt()); + self.binop(op.node, left, right) + }, ExprKind::Call(callee, []) => { // We only handle a few const functions for now. if let ExprKind::Path(qpath) = &callee.kind @@ -524,17 +643,20 @@ impl<'tcx> ConstEvalCtxt<'tcx> { }, ExprKind::Index(arr, index, _) => self.index(arr, index), ExprKind::AddrOf(_, _, inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))), - ExprKind::Field(local_expr, ref field) => { - let result = self.expr(local_expr); - if let Some(Constant::Adt(constant)) = &self.expr(local_expr) - && let ty::Adt(adt_def, _) = constant.ty().kind() + ExprKind::Field(base, ref field) + if let base_ty = self.typeck.expr_ty(base) + && match self.typeck.expr_adjustments(base) { + [] => true, + [.., a] => a.target == base_ty, + } + && let Some(Constant::Adt(constant)) = self.expr(base) + && let ty::Adt(adt_def, _) = *base_ty.kind() && adt_def.is_struct() - && let Some(desired_field) = field_of_struct(*adt_def, self.tcx, *constant, field) - { - mir_to_const(self.tcx, desired_field) - } else { - result - } + && let Some((desired_field, ty)) = + field_of_struct(adt_def, self.tcx, constant, base_ty, field.name) => + { + self.check_ctxt(field.span.ctxt()); + mir_to_const(self.tcx, desired_field, ty) }, _ => None, } @@ -547,19 +669,6 @@ impl<'tcx> ConstEvalCtxt<'tcx> { match e.kind { ExprKind::ConstBlock(ConstBlock { body, .. }) => self.eval_is_empty(self.tcx.hir_body(body).value), ExprKind::DropTemps(e) => self.eval_is_empty(e), - ExprKind::Path(ref qpath) => { - if !self - .typeck - .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.expr_ty(e), |self_, result| { - mir_is_empty(self_.tcx, result) - }) - }, ExprKind::Lit(lit) => { if is_direct_expn_of(e.span, sym::cfg).is_some() { None @@ -584,7 +693,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } #[expect(clippy::cast_possible_wrap)] - fn constant_not(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option> { + fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option { use self::Constant::{Bool, Int}; match *o { Bool(b) => Some(Bool(!b)), @@ -600,7 +709,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } - fn constant_negate(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option> { + fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option { use self::Constant::{F32, F64, Int}; match *o { Int(value) => { @@ -626,48 +735,128 @@ impl<'tcx> ConstEvalCtxt<'tcx> { /// Create `Some(Vec![..])` of all constants, unless there is any /// non-constant part. - fn multi(&self, vec: &[Expr<'_>]) -> Option>> { + fn multi(&self, vec: &[Expr<'_>]) -> Option> { vec.iter().map(|elem| self.expr(elem)).collect::>() } /// Lookup a possibly constant expression from an `ExprKind::Path` and apply a function on it. - fn fetch_path_and_apply(&self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option - where - F: FnOnce(&Self, mir::Const<'tcx>) -> Option, - { - let res = self.typeck.qpath_res(qpath, id); - match res { - Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { - // Check if this constant is based on `cfg!(..)`, - // which is NOT constant for our purposes. - if let Some(node) = self.tcx.hir_get_if_local(def_id) - && let Node::Item(Item { - kind: ItemKind::Const(.., body_id), - .. - }) = node - && let Node::Expr(Expr { - kind: ExprKind::Lit(_), - span, - .. - }) = self.tcx.hir_node(body_id.hir_id) - && is_direct_expn_of(*span, sym::cfg).is_some() - { - return None; - } - - let args = self.typeck.node_args(id); - let result = self - .tcx - .const_eval_resolve(self.typing_env, mir::UnevaluatedConst::new(def_id, args), qpath.span()) - .ok() - .map(|val| mir::Const::from_value(val, ty))?; - f(self, result) + #[expect(clippy::too_many_lines)] + fn fetch_path(&self, qpath: &QPath<'_>, id: HirId) -> Option { + // Resolve the path to a constant and check if that constant is known to + // not change based on the target. + // + // This should be replaced with an attribute at some point. + let did = match *qpath { + QPath::Resolved(None, path) + if path.span.ctxt() == self.ctxt.get() + && path.segments.iter().all(|s| self.ctxt.get() == s.ident.span.ctxt()) + && let Res::Def(DefKind::Const, did) = path.res + && (matches!( + self.tcx.get_diagnostic_name(did), + Some( + sym::f32_legacy_const_digits + | sym::f32_legacy_const_epsilon + | sym::f32_legacy_const_infinity + | sym::f32_legacy_const_mantissa_dig + | sym::f32_legacy_const_max + | sym::f32_legacy_const_max_10_exp + | sym::f32_legacy_const_max_exp + | sym::f32_legacy_const_min + | sym::f32_legacy_const_min_10_exp + | sym::f32_legacy_const_min_exp + | sym::f32_legacy_const_min_positive + | sym::f32_legacy_const_nan + | sym::f32_legacy_const_neg_infinity + | sym::f32_legacy_const_radix + | sym::f64_legacy_const_digits + | sym::f64_legacy_const_epsilon + | sym::f64_legacy_const_infinity + | sym::f64_legacy_const_mantissa_dig + | sym::f64_legacy_const_max + | sym::f64_legacy_const_max_10_exp + | sym::f64_legacy_const_max_exp + | sym::f64_legacy_const_min + | sym::f64_legacy_const_min_10_exp + | sym::f64_legacy_const_min_exp + | sym::f64_legacy_const_min_positive + | sym::f64_legacy_const_nan + | sym::f64_legacy_const_neg_infinity + | sym::f64_legacy_const_radix + | sym::u8_legacy_const_min + | sym::u16_legacy_const_min + | sym::u32_legacy_const_min + | sym::u64_legacy_const_min + | sym::u128_legacy_const_min + | sym::usize_legacy_const_min + | sym::u8_legacy_const_max + | sym::u16_legacy_const_max + | sym::u32_legacy_const_max + | sym::u64_legacy_const_max + | sym::u128_legacy_const_max + | sym::i8_legacy_const_min + | sym::i16_legacy_const_min + | sym::i32_legacy_const_min + | sym::i64_legacy_const_min + | sym::i128_legacy_const_min + | sym::i8_legacy_const_max + | sym::i16_legacy_const_max + | sym::i32_legacy_const_max + | sym::i64_legacy_const_max + | sym::i128_legacy_const_max + ) + ) || self.tcx.opt_parent(did).is_some_and(|parent| { + paths::F16_CONSTS.matches(&self.tcx, parent) + || paths::F32_CONSTS.matches(&self.tcx, parent) + || paths::F64_CONSTS.matches(&self.tcx, parent) + || paths::F128_CONSTS.matches(&self.tcx, parent) + })) => + { + did }, - _ => None, - } + QPath::TypeRelative(ty, const_name) + if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind + && let [.., ty_name] = ty_path.segments + && (matches!( + ty_name.ident.name, + sym::i8 + | sym::i16 + | sym::i32 + | sym::i64 + | sym::i128 + | sym::u8 + | sym::u16 + | sym::u32 + | sym::u64 + | sym::u128 + | sym::f32 + | sym::f64 + | sym::char + ) || (ty_name.ident.name == sym::usize && const_name.ident.name == sym::MIN)) + && const_name.ident.span.ctxt() == self.ctxt.get() + && ty.span.ctxt() == self.ctxt.get() + && ty_name.ident.span.ctxt() == self.ctxt.get() + && matches!(ty_path.res, Res::PrimTy(_)) + && let Some((DefKind::AssocConst, did)) = self.typeck.type_dependent_def(id) => + { + did + }, + _ if let Res::Def(DefKind::Const | DefKind::AssocConst, did) = self.typeck.qpath_res(qpath, id) => { + self.source.set(ConstantSource::NonLocal); + did + }, + _ => return None, + }; + + self.tcx + .const_eval_resolve( + self.typing_env, + mir::UnevaluatedConst::new(did, self.typeck.node_args(id)), + qpath.span(), + ) + .ok() } - fn index(&self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option> { + fn index(&self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option { let lhs = self.expr(lhs); let index = self.expr(index); @@ -697,7 +886,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } /// A block can only yield a constant if it has exactly one constant expression. - fn block(&self, block: &Block<'_>) -> Option> { + fn block(&self, block: &Block<'_>) -> Option { if block.stmts.is_empty() && let Some(expr) = block.expr { @@ -716,11 +905,11 @@ impl<'tcx> ConstEvalCtxt<'tcx> { .filter(|t| !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi)) .eq([OpenBrace]) { - self.source.set(ConstantSource::Constant); + self.source.set(ConstantSource::NonLocal); } } else { // Unable to access the source. Assume a non-local dependency. - self.source.set(ConstantSource::Constant); + self.source.set(ConstantSource::NonLocal); } } @@ -730,7 +919,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } - fn ifthenelse(&self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option> { + fn ifthenelse(&self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option { if let Some(Constant::Bool(b)) = self.expr(cond) { if b { self.expr(then) @@ -742,7 +931,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } - fn binop(&self, op: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> Option> { + fn binop(&self, op: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> Option { let l = self.expr(left)?; let r = self.expr(right); match (l, r) { @@ -778,6 +967,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { BinOpKind::BitXor => Some(zext(l ^ r)), BinOpKind::BitOr => Some(zext(l | r)), BinOpKind::BitAnd => Some(zext(l & r)), + // FIXME: f32/f64 currently consider `0.0` and `-0.0` as different. BinOpKind::Eq => Some(Constant::Bool(l == r)), BinOpKind::Ne => Some(Constant::Bool(l != r)), BinOpKind::Lt => Some(Constant::Bool(l < r)), @@ -856,14 +1046,10 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } -pub fn mir_to_const<'tcx>(tcx: TyCtxt<'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()) { - (ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() { - ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), +pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, val: ConstValue, ty: Ty<'tcx>) -> Option { + match (val, ty.kind()) { + (_, &ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(val)), + (ConstValue::Scalar(Scalar::Int(int)), _) => match ty.kind() { ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)), ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.to_bits(int.size()))), ty::Float(FloatTy::F16) => Some(Constant::F16(int.into())), @@ -877,7 +1063,6 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option let data = val.try_get_slice_bytes_for_diagnostics(tcx)?; String::from_utf8(data.to_owned()).ok().map(Constant::Str) }, - (_, ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(result)), (ConstValue::Indirect { alloc_id, offset }, ty::Array(sub_type, len)) => { let alloc = tcx.global_alloc(alloc_id).unwrap_memory().inner(); let len = len.try_to_target_usize(tcx)?; @@ -902,64 +1087,32 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option } } -fn mir_is_empty<'tcx>(tcx: TyCtxt<'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 = tcx.global_alloc(alloc_id).unwrap_memory().inner(); - let ptr_size = tcx.data_layout.pointer_size(); - if a.size() < offset + 2 * ptr_size { - // (partially) dangling reference - return None; - } - let len = a - .read_scalar(&tcx, alloc_range(offset + ptr_size, ptr_size), false) - .ok()? - .to_target_usize(&tcx) - .discard_err()?; - Some(len == 0) - } else { - None - } - }, - ty::Array(_, len) => Some(len.try_to_target_usize(tcx)? == 0), - _ => None, - }, - (ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(tcx)? == 0), - (ConstValue::ZeroSized, _) => Some(true), - _ => None, - } -} - fn field_of_struct<'tcx>( adt_def: ty::AdtDef<'tcx>, tcx: TyCtxt<'tcx>, - result: mir::Const<'tcx>, - field: &Ident, -) -> Option> { - if let mir::Const::Val(result, ty) = result - && let Some(dc) = tcx.try_destructure_mir_constant_for_user_output(result, ty) + value: ConstValue, + ty: Ty<'tcx>, + field: Symbol, +) -> Option<(ConstValue, Ty<'tcx>)> { + if let Some(dc) = tcx.try_destructure_mir_constant_for_user_output(value, ty) && let Some(dc_variant) = dc.variant && let Some(variant) = adt_def.variants().get(dc_variant) - && let Some(field_idx) = variant.fields.iter().position(|el| el.name == field.name) - && let Some(&(val, ty)) = dc.fields.get(field_idx) + && let Some(field_idx) = variant.fields.iter().position(|el| el.name == field) { - Some(mir::Const::Val(val, ty)) + dc.fields.get(field_idx).copied() } else { None } } /// If `expr` evaluates to an integer constant, return its value. -pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - if let Some(Constant::Int(value)) = ConstEvalCtxt::new(cx).eval_simple(expr) { +/// +/// The context argument is the context used to view the evaluated expression. e.g. when evaluating +/// the argument in `f(m!(1))` the context of the call expression should be used. This is need so +/// the const evaluator can see the `m` macro and marke the evaluation as non-local independant of +/// what the macro expands to. +pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> Option { + if let Some(Constant::Int(value)) = ConstEvalCtxt::new(cx).eval_local(expr, ctxt) { Some(value) } else { None @@ -967,7 +1120,12 @@ pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } /// Check if `expr` evaluates to an integer constant of 0. +/// +/// The context argument is the context used to view the evaluated expression. e.g. when evaluating +/// the argument in `f(m!(1))` the context of the call expression should be used. This is need so +/// the const evaluator can see the `m` macro and marke the evaluation as non-local independant of +/// what the macro expands to. #[inline] -pub fn is_zero_integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - integer_const(cx, expr) == Some(0) +pub fn is_zero_integer_const(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> bool { + integer_const(cx, expr, ctxt) == Some(0) } diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index bda28a663fb07..6f1bc28fbab81 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -454,7 +454,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - return Some(VecInitKind::Default); } else if name.ident.name == sym::with_capacity { let arg = args.first()?; - return match ConstEvalCtxt::new(cx).eval_simple(arg) { + return match ConstEvalCtxt::new(cx).eval_local(arg, expr.span.ctxt()) { Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)), _ => Some(VecInitKind::WithExprCapacity(arg.hir_id)), }; diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index b79e15cd7170b..a6d821c99c1b5 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -290,8 +290,10 @@ impl HirEqInterExpr<'_, '_, '_> { if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results && typeck_lhs.expr_ty(left) == typeck_rhs.expr_ty(right) && let (Some(l), Some(r)) = ( - ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_lhs).eval_simple(left), - ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_rhs).eval_simple(right), + ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_lhs) + .eval_local(left, self.left_ctxt), + ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_rhs) + .eval_local(right, self.right_ctxt), ) && l == r { @@ -842,7 +844,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { #[expect(clippy::too_many_lines)] pub fn hash_expr(&mut self, e: &Expr<'_>) { let simple_const = self.maybe_typeck_results.and_then(|typeck_results| { - ConstEvalCtxt::with_env(self.cx.tcx, self.cx.typing_env(), typeck_results).eval_simple(e) + ConstEvalCtxt::with_env(self.cx.tcx, self.cx.typing_env(), typeck_results).eval_local(e, e.span.ctxt()) }); // const hashing may result in the same hash as some unrelated node, so add a sort of diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index feadc0ecf659a..24864e8ef96dc 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -63,7 +63,6 @@ pub mod mir; pub mod msrvs; pub mod numeric_literal; pub mod paths; -pub mod ptr; pub mod qualify_min_const_fn; pub mod source; pub mod str_utils; @@ -99,7 +98,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefPath, DefPathData}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; -use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{ self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, @@ -127,8 +126,9 @@ use source::{SpanRangeExt, walk_span_to_context}; use visitors::{Visitable, for_each_unconsumed_temporary}; use crate::ast_utils::unordered_over; -use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; +use crate::consts::{ConstEvalCtxt, Constant}; use crate::higher::Range; +use crate::msrvs::Msrv; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -1423,11 +1423,9 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti let start_is_none_or_min = start.is_none_or(|start| { if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) - && let Some(min_const) = bnd_ty.numeric_min_val(cx.tcx) - && let Some(min_const) = mir_to_const(cx.tcx, min_const) && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start) { - start_const == min_const + start_const.is_numeric_min(cx.tcx, bnd_ty) } else { false } @@ -1436,11 +1434,9 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti RangeLimits::Closed => { if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) - && let Some(max_const) = bnd_ty.numeric_max_val(cx.tcx) - && let Some(max_const) = mir_to_const(cx.tcx, max_const) && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end) { - end_const == max_const + end_const.is_numeric_max(cx.tcx, bnd_ty) } else { false } @@ -1855,15 +1851,6 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, (conds, blocks) } -/// Checks if the given function kind is an async function. -pub fn is_async_fn(kind: FnKind<'_>) -> bool { - match kind { - FnKind::ItemFn(_, _, header) => header.asyncness.is_async(), - FnKind::Method(_, sig) => sig.header.asyncness.is_async(), - FnKind::Closure => false, - } -} - /// Peels away all the compiler generated code surrounding the body of an async closure. pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { if let ExprKind::Closure(&Closure { @@ -3659,3 +3646,8 @@ pub fn is_expr_async_block(expr: &Expr<'_>) -> bool { }) ) } + +/// Checks if the chosen edition and `msrv` allows using `if let` chains. +pub fn can_use_if_let_chains(cx: &LateContext<'_>, msrv: Msrv) -> bool { + cx.tcx.sess.edition().at_least_rust_2024() && msrv.meets(cx, msrvs::LET_CHAINS) +} diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 6e07ed9ffcc4d..62041fc631c04 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -28,9 +28,10 @@ msrv_aliases! { 1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL } 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } - 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP } + 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP, SPECIALIZED_TO_STRING_FOR_REFS } 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION, DURATION_ABS_DIFF } 1,80,0 { BOX_INTO_ITER, LAZY_CELL } + 1,79,0 { CONST_BLOCKS } 1,77,0 { C_STR_LITERALS } 1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT } 1,75,0 { OPTION_AS_SLICE } @@ -46,7 +47,7 @@ msrv_aliases! { 1,60,0 { ABS_DIFF } 1,59,0 { THREAD_LOCAL_CONST_INIT } 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY, CONST_RAW_PTR_DEREF } - 1,57,0 { MAP_WHILE } + 1,57,0 { MAP_WHILE, CONST_PANIC } 1,56,0 { CONST_FN_UNION } 1,55,0 { SEEK_REWIND } 1,54,0 { INTO_KEYS } @@ -72,12 +73,12 @@ msrv_aliases! { 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } 1,29,0 { ITER_FLATTEN } 1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF } - 1,27,0 { ITERATOR_TRY_FOLD } + 1,27,0 { ITERATOR_TRY_FOLD, DOUBLE_ENDED_ITERATOR_RFIND } 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN, POINTER_ADD_SUB_METHODS } 1,24,0 { IS_ASCII_DIGIT, PTR_NULL } 1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } - 1,16,0 { STR_REPEAT } + 1,16,0 { STR_REPEAT, RESULT_UNWRAP_OR_DEFAULT } 1,15,0 { MAYBE_BOUND_IN_WHERE } 1,13,0 { QUESTION_MARK_OPERATOR } } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index ea8cfc59356a7..5ab8e16d88ed3 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -13,6 +13,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::{ItemKind, Node, UseKind}; use rustc_lint::LateContext; use rustc_middle::ty::fast_reject::SimplifiedType; +use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::{FloatTy, IntTy, Ty, TyCtxt, UintTy}; use rustc_span::{Ident, STDLIB_STABLE_CRATES, Symbol}; use std::sync::OnceLock; @@ -74,8 +75,8 @@ impl PathLookup { } /// Returns the list of [`DefId`]s that the path resolves to - pub fn get(&self, cx: &LateContext<'_>) -> &[DefId] { - self.once.get_or_init(|| lookup_path(cx.tcx, self.ns, self.path)) + pub fn get<'tcx>(&self, tcx: &impl HasTyCtxt<'tcx>) -> &[DefId] { + self.once.get_or_init(|| lookup_path(tcx.tcx(), self.ns, self.path)) } /// Returns the single [`DefId`] that the path resolves to, this can only be used for paths into @@ -90,8 +91,8 @@ impl PathLookup { } /// Checks if the path resolves to the given `def_id` - pub fn matches(&self, cx: &LateContext<'_>, def_id: DefId) -> bool { - self.get(cx).contains(&def_id) + pub fn matches<'tcx>(&self, tcx: &impl HasTyCtxt<'tcx>, def_id: DefId) -> bool { + self.get(&tcx.tcx()).contains(&def_id) } /// Resolves `maybe_path` to a [`DefId`] and checks if the [`PathLookup`] matches it @@ -100,8 +101,8 @@ impl PathLookup { } /// Checks if the path resolves to `ty`'s definition, must be an `Adt` - pub fn matches_ty(&self, cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - ty.ty_adt_def().is_some_and(|adt| self.matches(cx, adt.did())) + pub fn matches_ty<'tcx>(&self, tcx: &impl HasTyCtxt<'tcx>, ty: Ty<'_>) -> bool { + ty.ty_adt_def().is_some_and(|adt| self.matches(&tcx.tcx(), adt.did())) } } @@ -126,6 +127,11 @@ path_macros! { macro_path: PathNS::Macro, } +pub static F16_CONSTS: PathLookup = type_path!(core::f16::consts); +pub static F32_CONSTS: PathLookup = type_path!(core::f32::consts); +pub static F64_CONSTS: PathLookup = type_path!(core::f64::consts); +pub static F128_CONSTS: PathLookup = type_path!(core::f128::consts); + // Paths in external crates pub static FUTURES_IO_ASYNCREADEXT: PathLookup = type_path!(futures_util::AsyncReadExt); pub static FUTURES_IO_ASYNCWRITEEXT: PathLookup = type_path!(futures_util::AsyncWriteExt); diff --git a/clippy_utils/src/ptr.rs b/clippy_utils/src/ptr.rs deleted file mode 100644 index 5847e916e340f..0000000000000 --- a/clippy_utils/src/ptr.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::source::snippet; -use crate::visitors::{Descend, for_each_expr_without_closures}; -use crate::{path_to_local_id, strip_pat_refs, sym}; -use core::ops::ControlFlow; -use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind}; -use rustc_lint::LateContext; -use rustc_span::{Span, Symbol}; -use std::borrow::Cow; - -pub fn get_spans( - cx: &LateContext<'_>, - opt_body_id: Option, - idx: usize, - replacements: &[(Symbol, &'static str)], -) -> Option)>> { - if let Some(body) = opt_body_id.map(|id| cx.tcx.hir_body(id)) { - if let PatKind::Binding(_, binding_id, _, _) = strip_pat_refs(body.params[idx].pat).kind { - extract_clone_suggestions(cx, binding_id, replacements, body) - } else { - Some(vec![]) - } - } else { - Some(vec![]) - } -} - -fn extract_clone_suggestions<'tcx>( - cx: &LateContext<'tcx>, - id: HirId, - replace: &[(Symbol, &'static str)], - body: &'tcx Body<'_>, -) -> Option)>> { - let mut spans = Vec::new(); - for_each_expr_without_closures(body, |e| { - if let ExprKind::MethodCall(seg, recv, [], _) = e.kind - && path_to_local_id(recv, id) - { - if seg.ident.name == sym::capacity { - return ControlFlow::Break(()); - } - for &(fn_name, suffix) in replace { - if seg.ident.name == fn_name { - spans.push((e.span, snippet(cx, recv.span, "_") + suffix)); - return ControlFlow::Continue(Descend::No); - } - } - } - ControlFlow::Continue(Descend::Yes) - }) - .is_none() - .then_some(spans) -} diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 7530d3bc7157d..2d0d4a5319f3c 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -111,8 +111,11 @@ generate! { clone_into, cloned, cognitive_complexity, + collapsible_else_if, + collapsible_if, collect, const_ptr, + consts, contains, copied, copy_from, @@ -189,8 +192,10 @@ generate! { is_none, is_none_or, is_ok, + is_partitioned, is_some, is_some_and, + is_sorted_by_key, isqrt, itertools, join, @@ -208,6 +213,7 @@ generate! { map_continue, map_or, map_or_else, + map_while, match_indices, matches, max, @@ -344,6 +350,7 @@ generate! { trim_start, trim_start_matches, truncate, + try_for_each, unreachable_pub, unsafe_removed_from_name, unused, diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index c03469c2b8851..ebf4f2cd3263c 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -21,9 +21,9 @@ use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ - self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg, GenericArgKind, GenericArgsRef, - GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, + self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg, + GenericArgKind, GenericArgsRef, GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, + TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index 55e588f5ec736..0d0b80c309ddd 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -22,6 +22,6 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.85" strip-ansi-escapes = "0.2.0" tar = "0.4" -toml = "0.7.3" +toml = "0.9.7" ureq = { version = "2.2", features = ["json"] } walkdir = "2.3" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 9c102de448200..e936f5dc3b7a5 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-09-18" +channel = "nightly-2025-10-06" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 6b6dfd7b81eab..71cd8a6c03cc9 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -405,11 +405,18 @@ fn ui_cargo_toml_metadata() { continue; } - let toml = fs::read_to_string(path).unwrap().parse::().unwrap(); - - let package = toml.as_table().unwrap().get("package").unwrap().as_table().unwrap(); - - let name = package.get("name").unwrap().as_str().unwrap().replace('-', "_"); + let toml = fs::read_to_string(path).unwrap(); + let toml = toml::de::DeTable::parse(&toml).unwrap(); + + let package = toml.get_ref().get("package").unwrap().get_ref().as_table().unwrap(); + + let name = package + .get("name") + .unwrap() + .as_ref() + .as_str() + .unwrap() + .replace('-', "_"); assert!( path.parent() .unwrap() @@ -421,7 +428,10 @@ fn ui_cargo_toml_metadata() { path.display(), ); - let publish = package.get("publish").and_then(toml::Value::as_bool).unwrap_or(true); + let publish = package + .get("publish") + .and_then(|x| x.get_ref().as_bool()) + .unwrap_or(true); assert!( !publish || publish_exceptions.contains(&path.parent().unwrap().to_path_buf()), "`{}` lacks `publish = false`", diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.stderr b/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.stderr new file mode 100644 index 0000000000000..c7490c5da027b --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.stderr @@ -0,0 +1,11 @@ +error: `mod.rs` files are required, found `src/foo.rs` + --> src/foo.rs:1:1 + | +1 | pub mod bar; + | ^ + | + = help: move `src/foo.rs` to `src/foo/mod.rs` + = note: `-D clippy::self-named-module-files` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::self_named_module_files)]` + +error: could not compile `duplicated-mod-names-14697` (lib) due to 1 previous error diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.toml b/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.toml new file mode 100644 index 0000000000000..569082f2f6599 --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/Cargo.toml @@ -0,0 +1,11 @@ +# Should trigger when multiple mods with the same name exist and not all of them follow self-named convention. +# See issue #14697. +[package] +name = "duplicated-mod-names-14697" +version = "0.1.0" +edition = "2024" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo.rs b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo.rs new file mode 100644 index 0000000000000..46f285ca47d69 --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo.rs @@ -0,0 +1 @@ +pub mod bar; diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo/bar.rs b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo/bar.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/foo/bar.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/lib.rs b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/lib.rs new file mode 100644 index 0000000000000..a85dae5748165 --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/lib.rs @@ -0,0 +1,4 @@ +#![warn(clippy::self_named_module_files)] + +pub mod foo; +pub mod other; diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/foo/mod.rs b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/foo/mod.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/foo/mod.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/mod.rs b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/mod.rs new file mode 100644 index 0000000000000..b52703b257400 --- /dev/null +++ b/tests/ui-cargo/module_style/duplicated_mod_names_14697/src/other/mod.rs @@ -0,0 +1 @@ +pub mod foo; diff --git a/tests/ui-cargo/module_style/fail_mod/Cargo.stderr b/tests/ui-cargo/module_style/fail_mod/Cargo.stderr index 902330e178530..f134943e69bfc 100644 --- a/tests/ui-cargo/module_style/fail_mod/Cargo.stderr +++ b/tests/ui-cargo/module_style/fail_mod/Cargo.stderr @@ -1,19 +1,19 @@ -error: `mod.rs` files are required, found `src/bad/inner.rs` - --> src/bad/inner.rs:1:1 +error: `mod.rs` files are required, found `src/bad/inner/stuff.rs` + --> src/bad/inner/stuff.rs:1:1 | -1 | pub mod stuff; +1 | pub mod most; | ^ | - = help: move `src/bad/inner.rs` to `src/bad/inner/mod.rs` + = help: move `src/bad/inner/stuff.rs` to `src/bad/inner/stuff/mod.rs` = note: `-D clippy::self-named-module-files` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::self_named_module_files)]` -error: `mod.rs` files are required, found `src/bad/inner/stuff.rs` - --> src/bad/inner/stuff.rs:1:1 +error: `mod.rs` files are required, found `src/bad/inner.rs` + --> src/bad/inner.rs:1:1 | -1 | pub mod most; +1 | pub mod stuff; | ^ | - = help: move `src/bad/inner/stuff.rs` to `src/bad/inner/stuff/mod.rs` + = help: move `src/bad/inner.rs` to `src/bad/inner/mod.rs` error: could not compile `fail-mod` (bin "fail-mod") due to 2 previous errors diff --git a/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/Cargo.toml b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/Cargo.toml new file mode 100644 index 0000000000000..5c2fabd2283d3 --- /dev/null +++ b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/Cargo.toml @@ -0,0 +1,10 @@ +# Should not produce FP when irrelavant path segment shares the same name with module. +# See issue #10271 and #11916. +[package] +name = "segment-with-mod-name-10271-11916" +version = "0.1.0" +edition = "2024" +publish = false + +[workspace] +members = ["foo/bar"] \ No newline at end of file diff --git a/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/Cargo.toml b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/Cargo.toml new file mode 100644 index 0000000000000..1f68c0dccac54 --- /dev/null +++ b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "bar" +version = "0.1.0" +edition = "2024" +publish = false diff --git a/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/foo.rs b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/foo.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/foo.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/lib.rs b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/lib.rs new file mode 100644 index 0000000000000..b52703b257400 --- /dev/null +++ b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/foo/bar/src/lib.rs @@ -0,0 +1 @@ +pub mod foo; diff --git a/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/src/lib.rs b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/src/lib.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/module_style/segment_with_mod_name_10271_11916/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/with_path_attr_mod/Cargo.toml b/tests/ui-cargo/module_style/with_path_attr_mod/Cargo.toml new file mode 100644 index 0000000000000..d867377545e15 --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_mod/Cargo.toml @@ -0,0 +1,10 @@ +# Should not lint mod tagged with `#[path = ...]` +[package] +name = "with-path-attr-mod" +version = "0.1.0" +edition = "2024" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/ui-cargo/module_style/with_path_attr_mod/src/bar/mod.rs b/tests/ui-cargo/module_style/with_path_attr_mod/src/bar/mod.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_mod/src/bar/mod.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/with_path_attr_mod/src/lib.rs b/tests/ui-cargo/module_style/with_path_attr_mod/src/lib.rs new file mode 100644 index 0000000000000..a5c2109ece7db --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_mod/src/lib.rs @@ -0,0 +1,4 @@ +#![warn(clippy::mod_module_files)] + +#[path = "bar/mod.rs"] +pub mod foo; diff --git a/tests/ui-cargo/module_style/with_path_attr_no_mod/Cargo.toml b/tests/ui-cargo/module_style/with_path_attr_no_mod/Cargo.toml new file mode 100644 index 0000000000000..ddf2ac394cddc --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_no_mod/Cargo.toml @@ -0,0 +1,10 @@ +# Should not lint mod tagged with `#[path = ...]` +[package] +name = "with-path-attr-no-mod" +version = "0.1.0" +edition = "2024" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/ui-cargo/module_style/with_path_attr_no_mod/src/bar.rs b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/bar.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/bar.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/module_style/with_path_attr_no_mod/src/foo.rs b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/foo.rs new file mode 100644 index 0000000000000..3b12aefa3d5fe --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/foo.rs @@ -0,0 +1,2 @@ +#[path = "bar.rs"] +mod bar; diff --git a/tests/ui-cargo/module_style/with_path_attr_no_mod/src/lib.rs b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/lib.rs new file mode 100644 index 0000000000000..bf2a5d0193350 --- /dev/null +++ b/tests/ui-cargo/module_style/with_path_attr_no_mod/src/lib.rs @@ -0,0 +1,3 @@ +#![warn(clippy::self_named_module_files)] + +pub mod foo; diff --git a/tests/ui-toml/collapsible_if/collapsible_else_if.fixed b/tests/ui-toml/collapsible_if/collapsible_else_if.fixed index 0dc0fc230c8de..ec45dfd2033ad 100644 --- a/tests/ui-toml/collapsible_if/collapsible_else_if.fixed +++ b/tests/ui-toml/collapsible_if/collapsible_else_if.fixed @@ -1,7 +1,7 @@ #![allow(clippy::eq_op, clippy::nonminimal_bool)] +#![warn(clippy::collapsible_if)] #[rustfmt::skip] -#[warn(clippy::collapsible_if)] fn main() { let (x, y) = ("hello", "world"); @@ -48,3 +48,20 @@ fn main() { } //~^^^^^^ collapsible_else_if } + +fn issue_13365() { + // the comments don't stop us from linting, so the the `expect` *will* be fulfilled + if true { + } else { + // some other text before + #[expect(clippy::collapsible_else_if)] + if false {} + } + + if true { + } else { + #[expect(clippy::collapsible_else_if)] + // some other text after + if false {} + } +} diff --git a/tests/ui-toml/collapsible_if/collapsible_else_if.rs b/tests/ui-toml/collapsible_if/collapsible_else_if.rs index 8344c122f16c8..54315a3c32bf9 100644 --- a/tests/ui-toml/collapsible_if/collapsible_else_if.rs +++ b/tests/ui-toml/collapsible_if/collapsible_else_if.rs @@ -1,7 +1,7 @@ #![allow(clippy::eq_op, clippy::nonminimal_bool)] +#![warn(clippy::collapsible_if)] #[rustfmt::skip] -#[warn(clippy::collapsible_if)] fn main() { let (x, y) = ("hello", "world"); @@ -53,3 +53,20 @@ fn main() { } //~^^^^^^ collapsible_else_if } + +fn issue_13365() { + // the comments don't stop us from linting, so the the `expect` *will* be fulfilled + if true { + } else { + // some other text before + #[expect(clippy::collapsible_else_if)] + if false {} + } + + if true { + } else { + #[expect(clippy::collapsible_else_if)] + // some other text after + if false {} + } +} diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs index ecb43dc34a8a0..b28e46af0a467 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs @@ -1,5 +1,3 @@ -#![allow(clippy::uninlined_format_args)] - fn main() {} #[warn(clippy::cognitive_complexity)] @@ -8,7 +6,7 @@ fn cognitive_complexity() { let x = vec![1, 2, 3]; for i in x { if i == 1 { - println!("{}", i); + println!("{i}"); } } } diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr index 627498dc175cd..95b0508189e9c 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr @@ -11,7 +11,7 @@ LL | blacklisted-names = [ "..", "wibble" ] | ^^^^^^^^^^^^^^^^^ error: the function has a cognitive complexity of (3/2) - --> tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs:6:4 + --> tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs:4:4 | LL | fn cognitive_complexity() { | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed index 2877871d0bf4c..36540bf1dcf73 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed @@ -1,4 +1,3 @@ -#![allow(clippy::uninlined_format_args)] #![deny(clippy::index_refutable_slice)] fn below_limit() { diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs index f958b92a102a3..da76bb20fd961 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs @@ -1,4 +1,3 @@ -#![allow(clippy::uninlined_format_args)] #![deny(clippy::index_refutable_slice)] fn below_limit() { diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr index e1a8941e102f5..022deb330e6e3 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr @@ -1,11 +1,11 @@ error: this binding can be a slice pattern to avoid indexing - --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:6:17 + --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:5:17 | LL | if let Some(slice) = slice { | ^^^^^ | note: the lint level is defined here - --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:2:9 + --> tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs:1:9 | LL | #![deny(clippy::index_refutable_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed index 8da607ec65842..419e62f92f469 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed @@ -67,3 +67,11 @@ fn main() { printlnfoo!["test if printlnfoo is triggered by println"]; } + +#[rustfmt::skip] +#[expect(clippy::no_effect)] +fn issue9913() { + println!("hello world"); + [0]; // separate statement, not indexing into the result of println. + //~^^ nonstandard_macro_braces +} diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs index e35844a209fa5..b0bbced4ea3c2 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs @@ -67,3 +67,11 @@ fn main() { printlnfoo!["test if printlnfoo is triggered by println"]; } + +#[rustfmt::skip] +#[expect(clippy::no_effect)] +fn issue9913() { + println! {"hello world"} + [0]; // separate statement, not indexing into the result of println. + //~^^ nonstandard_macro_braces +} diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr index fda6addc7aa35..87325f05c9bcd 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr @@ -54,5 +54,11 @@ error: use of irregular braces for `eprint!` macro LL | eprint!("test if user config overrides defaults"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `eprint!["test if user config overrides defaults"]` -error: aborting due to 8 previous errors +error: use of irregular braces for `println!` macro + --> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:74:5 + | +LL | println! {"hello world"} + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `println!("hello world");` + +error: aborting due to 9 previous errors diff --git a/tests/ui/assertions_on_constants.rs b/tests/ui/assertions_on_constants.rs index c2516c5414753..f467d4966aef7 100644 --- a/tests/ui/assertions_on_constants.rs +++ b/tests/ui/assertions_on_constants.rs @@ -42,8 +42,8 @@ fn main() { assert_const!(3); assert_const!(-1); - // Don't lint if based on `cfg!(..)`: assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf"))); + //~^ assertions_on_constants let flag: bool = cfg!(not(feature = "asdf")); assert!(flag); @@ -62,9 +62,37 @@ fn main() { const _: () = assert!(N.is_power_of_two()); } +const C: bool = true; + const _: () = { assert!(true); //~^ assertions_on_constants assert!(8 == (7 + 1)); + //~^ assertions_on_constants + + assert!(C); }; + +#[clippy::msrv = "1.57"] +fn _f1() { + assert!(C); + //~^ assertions_on_constants +} + +#[clippy::msrv = "1.56"] +fn _f2() { + assert!(C); +} + +#[clippy::msrv = "1.79"] +fn _f3() { + assert!(C); + //~^ assertions_on_constants +} + +#[clippy::msrv = "1.78"] +fn _f4() { + assert!(C); + //~^ assertions_on_constants +} diff --git a/tests/ui/assertions_on_constants.stderr b/tests/ui/assertions_on_constants.stderr index 8b7440ec4832c..a996c41b69420 100644 --- a/tests/ui/assertions_on_constants.stderr +++ b/tests/ui/assertions_on_constants.stderr @@ -1,100 +1,140 @@ -error: `assert!(true)` will be optimized out by the compiler +error: this assertion is always `true` --> tests/ui/assertions_on_constants.rs:10:5 | LL | assert!(true); | ^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion = note: `-D clippy::assertions-on-constants` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::assertions_on_constants)]` -error: `assert!(false)` should probably be replaced +error: this assertion is always `false` --> tests/ui/assertions_on_constants.rs:13:5 | LL | assert!(false); | ^^^^^^^^^^^^^^ | - = help: use `panic!()` or `unreachable!()` + = help: replace this with `panic!()` or `unreachable!()` -error: `assert!(true)` will be optimized out by the compiler +error: this assertion is always `true` --> tests/ui/assertions_on_constants.rs:16:5 | LL | assert!(true, "true message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion -error: `assert!(false, ..)` should probably be replaced +error: this assertion is always `false` --> tests/ui/assertions_on_constants.rs:19:5 | LL | assert!(false, "false message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use `panic!(..)` or `unreachable!(..)` + = help: replace this with `panic!()` or `unreachable!()` -error: `assert!(false, ..)` should probably be replaced +error: this assertion is always `false` --> tests/ui/assertions_on_constants.rs:23:5 | LL | assert!(false, "{}", msg.to_uppercase()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use `panic!(..)` or `unreachable!(..)` + = help: replace this with `panic!()` or `unreachable!()` -error: `assert!(true)` will be optimized out by the compiler +error: this assertion has a constant value --> tests/ui/assertions_on_constants.rs:27:5 | LL | assert!(B); | ^^^^^^^^^^ | - = help: remove it + = help: consider moving this into a const block: `const { assert!(..) }` -error: `assert!(false)` should probably be replaced +error: this assertion has a constant value --> tests/ui/assertions_on_constants.rs:31:5 | LL | assert!(C); | ^^^^^^^^^^ | - = help: use `panic!()` or `unreachable!()` + = help: consider moving this into a const block: `const { assert!(..) }` -error: `assert!(false, ..)` should probably be replaced +error: this assertion has a constant value --> tests/ui/assertions_on_constants.rs:34:5 | LL | assert!(C, "C message"); | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use `panic!(..)` or `unreachable!(..)` + = help: consider moving this into a const block: `const { assert!(..) }` -error: `debug_assert!(true)` will be optimized out by the compiler +error: this assertion is always `true` --> tests/ui/assertions_on_constants.rs:37:5 | LL | debug_assert!(true); | ^^^^^^^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion -error: `assert!(true)` will be optimized out by the compiler +error: this assertion has a constant value + --> tests/ui/assertions_on_constants.rs:45:5 + | +LL | assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf"))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider moving this into a const block: `const { assert!(..) }` + +error: this assertion is always `true` --> tests/ui/assertions_on_constants.rs:54:19 | LL | const _: () = assert!(true); | ^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion -error: `assert!(true)` will be optimized out by the compiler +error: this assertion is always `true` --> tests/ui/assertions_on_constants.rs:57:5 | LL | assert!(8 == (7 + 1)); | ^^^^^^^^^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion -error: `assert!(true)` will be optimized out by the compiler - --> tests/ui/assertions_on_constants.rs:66:5 +error: this assertion is always `true` + --> tests/ui/assertions_on_constants.rs:68:5 | LL | assert!(true); | ^^^^^^^^^^^^^ | - = help: remove it + = help: remove the assertion + +error: this assertion is always `true` + --> tests/ui/assertions_on_constants.rs:71:5 + | +LL | assert!(8 == (7 + 1)); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove the assertion + +error: this assertion has a constant value + --> tests/ui/assertions_on_constants.rs:79:5 + | +LL | assert!(C); + | ^^^^^^^^^^ + | + = help: consider moving this to an anonymous constant: `const _: () = { assert!(..); }` + +error: this assertion has a constant value + --> tests/ui/assertions_on_constants.rs:90:5 + | +LL | assert!(C); + | ^^^^^^^^^^ + | + = help: consider moving this into a const block: `const { assert!(..) }` + +error: this assertion has a constant value + --> tests/ui/assertions_on_constants.rs:96:5 + | +LL | assert!(C); + | ^^^^^^^^^^ + | + = help: consider moving this to an anonymous constant: `const _: () = { assert!(..); }` -error: aborting due to 12 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/auxiliary/option_helpers.rs b/tests/ui/auxiliary/option_helpers.rs index f9bc9436b0792..796d4dabf04d2 100644 --- a/tests/ui/auxiliary/option_helpers.rs +++ b/tests/ui/auxiliary/option_helpers.rs @@ -25,6 +25,10 @@ impl IteratorFalsePositives { self } + pub fn next_back(self) -> IteratorFalsePositives { + self + } + pub fn find(self) -> Option { Some(self.foo) } diff --git a/tests/ui/bind_instead_of_map.fixed b/tests/ui/bind_instead_of_map.fixed index 80e010e2dfd74..fa35a01242d1d 100644 --- a/tests/ui/bind_instead_of_map.fixed +++ b/tests/ui/bind_instead_of_map.fixed @@ -1,5 +1,4 @@ #![deny(clippy::bind_instead_of_map)] -#![allow(clippy::uninlined_format_args)] // need a main anyway, use it get rid of unused warnings too pub fn main() { diff --git a/tests/ui/bind_instead_of_map.rs b/tests/ui/bind_instead_of_map.rs index 09aa8480cbd9e..403077e72ff98 100644 --- a/tests/ui/bind_instead_of_map.rs +++ b/tests/ui/bind_instead_of_map.rs @@ -1,5 +1,4 @@ #![deny(clippy::bind_instead_of_map)] -#![allow(clippy::uninlined_format_args)] // need a main anyway, use it get rid of unused warnings too pub fn main() { diff --git a/tests/ui/bind_instead_of_map.stderr b/tests/ui/bind_instead_of_map.stderr index 08f85fb58549c..3f8d631591e93 100644 --- a/tests/ui/bind_instead_of_map.stderr +++ b/tests/ui/bind_instead_of_map.stderr @@ -1,5 +1,5 @@ error: using `Option.and_then(Some)`, which is a no-op - --> tests/ui/bind_instead_of_map.rs:8:13 + --> tests/ui/bind_instead_of_map.rs:7:13 | LL | let _ = x.and_then(Some); | ^^^^^^^^^^^^^^^^ help: use the expression directly: `x` @@ -11,13 +11,13 @@ LL | #![deny(clippy::bind_instead_of_map)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` - --> tests/ui/bind_instead_of_map.rs:10:13 + --> tests/ui/bind_instead_of_map.rs:9:13 | LL | let _ = x.and_then(|o| Some(o + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.map(|o| o + 1)` error: using `Result.and_then(Ok)`, which is a no-op - --> tests/ui/bind_instead_of_map.rs:17:13 + --> tests/ui/bind_instead_of_map.rs:16:13 | LL | let _ = x.and_then(Ok); | ^^^^^^^^^^^^^^ help: use the expression directly: `x` diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs index e848f0601e32d..1646b5705b6dd 100644 --- a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs @@ -1,6 +1,5 @@ #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] #![allow(dead_code)] -#![allow(clippy::uninlined_format_args)] //@no-rustfix // branches_sharing_code at the top and bottom of the if blocks @@ -70,7 +69,7 @@ fn complexer_example() { let b = 0xffff00ff; let e_id = gen_id(a, b); - println!("From the a `{}` to the b `{}`", a, b); + println!("From the a `{a}` to the b `{b}`"); let pack = DataPack { id: e_id, @@ -83,7 +82,7 @@ fn complexer_example() { let b = 0xffff00ff; let e_id = gen_id(a, b); - println!("The new ID is '{}'", e_id); + println!("The new ID is '{e_id}'"); let pack = DataPack { id: e_id, diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr index 40f3453edb9ae..f5c51f7888d04 100644 --- a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr @@ -1,5 +1,5 @@ error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:17:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:16:5 | LL | / if x == 7 { LL | | @@ -10,7 +10,7 @@ LL | | let _overlap_end = 2 * t; | |_________________________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:31:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:30:5 | LL | / let _u = 9; LL | | } @@ -34,7 +34,7 @@ LL + let _u = 9; | error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:35:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:34:5 | LL | / if x == 99 { LL | | @@ -45,7 +45,7 @@ LL | | let _overlap_middle = r * r; | |____________________________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:48:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:47:5 | LL | / let _overlap_end = r * r * r; LL | | let z = "end"; @@ -67,7 +67,7 @@ LL + let z = "end"; | error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:66:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:65:5 | LL | / if (x > 7 && y < 13) || (x + y) % 2 == 1 { LL | | @@ -78,7 +78,7 @@ LL | | let e_id = gen_id(a, b); | |________________________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:88:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:87:5 | LL | / let pack = DataPack { LL | | id: e_id, @@ -108,7 +108,7 @@ LL + process_data(pack); | error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:101:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:100:5 | LL | / let _ = if x == 7 { ... | @@ -116,7 +116,7 @@ LL | | let _ = 19; | |___________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:112:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:111:5 | LL | / x << 2 LL | | }; @@ -134,7 +134,7 @@ LL ~ x << 2; | error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:115:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:114:5 | LL | / if x == 9 { ... | @@ -142,7 +142,7 @@ LL | | let _ = 17; | |___________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:126:5 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:125:5 | LL | / x * 4 LL | | } @@ -160,7 +160,7 @@ LL + x * 4 | error: all if blocks contain the same code at both the start and the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:158:9 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:157:9 | LL | / if false { LL | | @@ -168,7 +168,7 @@ LL | | let x = 1; | |______________________^ | note: this code is shared at the end - --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:166:9 + --> tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs:165:9 | LL | / let y = 1; LL | | } diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed index b55c22f5ca83c..44cb66a8d1848 100644 --- a/tests/ui/cast_abs_to_unsigned.fixed +++ b/tests/ui/cast_abs_to_unsigned.fixed @@ -1,11 +1,11 @@ #![warn(clippy::cast_abs_to_unsigned)] -#![allow(clippy::uninlined_format_args, unused)] +#![allow(unused)] fn main() { let x: i32 = -42; let y: u32 = x.unsigned_abs(); //~^ cast_abs_to_unsigned - println!("The absolute value of {} is {}", x, y); + println!("The absolute value of {x} is {y}"); let a: i32 = -3; let _: usize = a.unsigned_abs() as usize; diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs index 466aa6aeb1fbb..555b9090fe437 100644 --- a/tests/ui/cast_abs_to_unsigned.rs +++ b/tests/ui/cast_abs_to_unsigned.rs @@ -1,11 +1,11 @@ #![warn(clippy::cast_abs_to_unsigned)] -#![allow(clippy::uninlined_format_args, unused)] +#![allow(unused)] fn main() { let x: i32 = -42; let y: u32 = x.abs() as u32; //~^ cast_abs_to_unsigned - println!("The absolute value of {} is {}", x, y); + println!("The absolute value of {x} is {y}"); let a: i32 = -3; let _: usize = a.abs() as usize; diff --git a/tests/ui/checked_unwrap/if_let_chains.rs b/tests/ui/checked_unwrap/if_let_chains.rs new file mode 100644 index 0000000000000..cfa7715965cdb --- /dev/null +++ b/tests/ui/checked_unwrap/if_let_chains.rs @@ -0,0 +1,24 @@ +//@require-annotations-for-level: ERROR +#![deny(clippy::unnecessary_unwrap)] + +#[clippy::msrv = "1.85"] +fn if_let_chains_unsupported(a: Option, b: Option) { + if a.is_none() || b.is_none() { + println!("a or b is not set"); + } else { + println!("the value of a is {}", a.unwrap()); + //~^ unnecessary_unwrap + //~| HELP: try using `match` + } +} + +#[clippy::msrv = "1.88"] +fn if_let_chains_supported(a: Option, b: Option) { + if a.is_none() || b.is_none() { + println!("a or b is not set"); + } else { + println!("the value of a is {}", a.unwrap()); + //~^ unnecessary_unwrap + //~| HELP: try using `if let` or `match` + } +} diff --git a/tests/ui/checked_unwrap/if_let_chains.stderr b/tests/ui/checked_unwrap/if_let_chains.stderr new file mode 100644 index 0000000000000..8a4137de37a3c --- /dev/null +++ b/tests/ui/checked_unwrap/if_let_chains.stderr @@ -0,0 +1,29 @@ +error: called `unwrap` on `a` after checking its variant with `is_none` + --> tests/ui/checked_unwrap/if_let_chains.rs:9:42 + | +LL | if a.is_none() || b.is_none() { + | ----------- the check is happening here +... +LL | println!("the value of a is {}", a.unwrap()); + | ^^^^^^^^^^ + | + = help: try using `match` +note: the lint level is defined here + --> tests/ui/checked_unwrap/if_let_chains.rs:2:9 + | +LL | #![deny(clippy::unnecessary_unwrap)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `a` after checking its variant with `is_none` + --> tests/ui/checked_unwrap/if_let_chains.rs:20:42 + | +LL | if a.is_none() || b.is_none() { + | ----------- the check is happening here +... +LL | println!("the value of a is {}", a.unwrap()); + | ^^^^^^^^^^ + | + = help: try using `if let` or `match` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/clone_on_copy.fixed b/tests/ui/clone_on_copy.fixed index 2dd8af1525150..469121bbd740f 100644 --- a/tests/ui/clone_on_copy.fixed +++ b/tests/ui/clone_on_copy.fixed @@ -1,25 +1,16 @@ +#![warn(clippy::clone_on_copy)] #![allow( - unused, clippy::redundant_clone, clippy::deref_addrof, clippy::no_effect, clippy::unnecessary_operation, - clippy::vec_init_then_push, clippy::toplevel_ref_arg, clippy::needless_borrow )] use std::cell::RefCell; -use std::rc::{self, Rc}; -use std::sync::{self, Arc}; -fn main() {} - -fn is_ascii(ch: char) -> bool { - ch.is_ascii() -} - -fn clone_on_copy() -> Option<(i32)> { +fn main() { 42; //~^ clone_on_copy @@ -65,20 +56,66 @@ fn clone_on_copy() -> Option<(i32)> { let x = 42; let ref y = x.clone(); // ok, binds by reference let ref mut y = x.clone(); // ok, binds by reference +} + +mod issue3052 { + struct A; + struct B; + struct C; + struct D; + #[derive(Copy, Clone)] + struct E; + + macro_rules! impl_deref { + ($src:ident, $dst:ident) => { + impl std::ops::Deref for $src { + type Target = $dst; + fn deref(&self) -> &Self::Target { + &$dst + } + } + }; + } + + impl_deref!(A, B); + impl_deref!(B, C); + impl_deref!(C, D); + impl std::ops::Deref for D { + type Target = &'static E; + fn deref(&self) -> &Self::Target { + &&E + } + } + + fn go1() { + let a = A; + let _: E = *****a; + //~^ clone_on_copy + + let _: E = *****a; + } +} + +fn issue4348() { + fn is_ascii(ch: char) -> bool { + ch.is_ascii() + } - // Issue #4348 let mut x = 43; let _ = &x.clone(); // ok, getting a ref 'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate is_ascii('z'); //~^ clone_on_copy +} - // Issue #5436 +#[expect(clippy::vec_init_then_push)] +fn issue5436() { let mut vec = Vec::new(); vec.push(42); //~^ clone_on_copy +} - // Issue #9277 +fn issue9277() -> Option { let opt: &Option = &None; let value = (*opt)?; // operator precedence needed (*opt)? // diff --git a/tests/ui/clone_on_copy.rs b/tests/ui/clone_on_copy.rs index a371afb04a78f..b05f1d3aa35e9 100644 --- a/tests/ui/clone_on_copy.rs +++ b/tests/ui/clone_on_copy.rs @@ -1,25 +1,16 @@ +#![warn(clippy::clone_on_copy)] #![allow( - unused, clippy::redundant_clone, clippy::deref_addrof, clippy::no_effect, clippy::unnecessary_operation, - clippy::vec_init_then_push, clippy::toplevel_ref_arg, clippy::needless_borrow )] use std::cell::RefCell; -use std::rc::{self, Rc}; -use std::sync::{self, Arc}; -fn main() {} - -fn is_ascii(ch: char) -> bool { - ch.is_ascii() -} - -fn clone_on_copy() -> Option<(i32)> { +fn main() { 42.clone(); //~^ clone_on_copy @@ -65,20 +56,66 @@ fn clone_on_copy() -> Option<(i32)> { let x = 42; let ref y = x.clone(); // ok, binds by reference let ref mut y = x.clone(); // ok, binds by reference +} + +mod issue3052 { + struct A; + struct B; + struct C; + struct D; + #[derive(Copy, Clone)] + struct E; + + macro_rules! impl_deref { + ($src:ident, $dst:ident) => { + impl std::ops::Deref for $src { + type Target = $dst; + fn deref(&self) -> &Self::Target { + &$dst + } + } + }; + } + + impl_deref!(A, B); + impl_deref!(B, C); + impl_deref!(C, D); + impl std::ops::Deref for D { + type Target = &'static E; + fn deref(&self) -> &Self::Target { + &&E + } + } + + fn go1() { + let a = A; + let _: E = a.clone(); + //~^ clone_on_copy + + let _: E = *****a; + } +} + +fn issue4348() { + fn is_ascii(ch: char) -> bool { + ch.is_ascii() + } - // Issue #4348 let mut x = 43; let _ = &x.clone(); // ok, getting a ref 'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate is_ascii('z'.clone()); //~^ clone_on_copy +} - // Issue #5436 +#[expect(clippy::vec_init_then_push)] +fn issue5436() { let mut vec = Vec::new(); vec.push(42.clone()); //~^ clone_on_copy +} - // Issue #9277 +fn issue9277() -> Option { let opt: &Option = &None; let value = opt.clone()?; // operator precedence needed (*opt)? // diff --git a/tests/ui/clone_on_copy.stderr b/tests/ui/clone_on_copy.stderr index 92cdd635d20a8..c87d1d488dde5 100644 --- a/tests/ui/clone_on_copy.stderr +++ b/tests/ui/clone_on_copy.stderr @@ -1,5 +1,5 @@ error: using `clone` on type `i32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:23:5 + --> tests/ui/clone_on_copy.rs:14:5 | LL | 42.clone(); | ^^^^^^^^^^ help: try removing the `clone` call: `42` @@ -8,52 +8,58 @@ LL | 42.clone(); = help: to override `-D warnings` add `#[allow(clippy::clone_on_copy)]` error: using `clone` on type `i32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:28:5 + --> tests/ui/clone_on_copy.rs:19:5 | LL | (&42).clone(); | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)` error: using `clone` on type `i32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:32:5 + --> tests/ui/clone_on_copy.rs:23:5 | LL | rc.borrow().clone(); | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()` error: using `clone` on type `u32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:36:5 + --> tests/ui/clone_on_copy.rs:27:5 | LL | x.clone().rotate_left(1); | ^^^^^^^^^ help: try removing the `clone` call: `x` error: using `clone` on type `i32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:51:5 + --> tests/ui/clone_on_copy.rs:42:5 | LL | m!(42).clone(); | ^^^^^^^^^^^^^^ help: try removing the `clone` call: `m!(42)` error: using `clone` on type `[u32; 2]` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:62:5 + --> tests/ui/clone_on_copy.rs:53:5 | LL | x.clone()[0]; | ^^^^^^^^^ help: try dereferencing it: `(*x)` +error: using `clone` on type `E` which implements the `Copy` trait + --> tests/ui/clone_on_copy.rs:92:20 + | +LL | let _: E = a.clone(); + | ^^^^^^^^^ help: try dereferencing it: `*****a` + error: using `clone` on type `char` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:73:14 + --> tests/ui/clone_on_copy.rs:107:14 | LL | is_ascii('z'.clone()); | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'` error: using `clone` on type `i32` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:78:14 + --> tests/ui/clone_on_copy.rs:114:14 | LL | vec.push(42.clone()); | ^^^^^^^^^^ help: try removing the `clone` call: `42` error: using `clone` on type `Option` which implements the `Copy` trait - --> tests/ui/clone_on_copy.rs:83:17 + --> tests/ui/clone_on_copy.rs:120:17 | LL | let value = opt.clone()?; // operator precedence needed (*opt)? | ^^^^^^^^^^^ help: try dereferencing it: `(*opt)` -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/clone_on_ref_ptr.fixed b/tests/ui/clone_on_ref_ptr.fixed new file mode 100644 index 0000000000000..8ef4b36566363 --- /dev/null +++ b/tests/ui/clone_on_ref_ptr.fixed @@ -0,0 +1,51 @@ +#![warn(clippy::clone_on_ref_ptr)] + +use std::rc::{Rc, Weak as RcWeak}; +use std::sync::{Arc, Weak as ArcWeak}; + +fn main() {} + +fn clone_on_ref_ptr(rc: Rc, rc_weak: RcWeak, arc: Arc, arc_weak: ArcWeak) { + std::rc::Rc::::clone(&rc); + //~^ clone_on_ref_ptr + std::rc::Weak::::clone(&rc_weak); + //~^ clone_on_ref_ptr + std::sync::Arc::::clone(&arc); + //~^ clone_on_ref_ptr + std::sync::Weak::::clone(&arc_weak); + //~^ clone_on_ref_ptr + + Rc::clone(&rc); + Arc::clone(&arc); + RcWeak::clone(&rc_weak); + ArcWeak::clone(&arc_weak); +} + +trait SomeTrait {} +struct SomeImpl; +impl SomeTrait for SomeImpl {} + +fn trait_object() { + let x = Arc::new(SomeImpl); + let _: Arc = std::sync::Arc::::clone(&x); + //~^ clone_on_ref_ptr +} + +mod issue2076 { + use std::rc::Rc; + + macro_rules! try_opt { + ($expr: expr) => { + match $expr { + Some(value) => value, + None => return None, + } + }; + } + + fn func() -> Option> { + let rc = Rc::new(42); + Some(std::rc::Rc::::clone(&try_opt!(Some(rc)))) + //~^ clone_on_ref_ptr + } +} diff --git a/tests/ui/clone_on_ref_ptr.rs b/tests/ui/clone_on_ref_ptr.rs new file mode 100644 index 0000000000000..fbd787099aee3 --- /dev/null +++ b/tests/ui/clone_on_ref_ptr.rs @@ -0,0 +1,51 @@ +#![warn(clippy::clone_on_ref_ptr)] + +use std::rc::{Rc, Weak as RcWeak}; +use std::sync::{Arc, Weak as ArcWeak}; + +fn main() {} + +fn clone_on_ref_ptr(rc: Rc, rc_weak: RcWeak, arc: Arc, arc_weak: ArcWeak) { + rc.clone(); + //~^ clone_on_ref_ptr + rc_weak.clone(); + //~^ clone_on_ref_ptr + arc.clone(); + //~^ clone_on_ref_ptr + arc_weak.clone(); + //~^ clone_on_ref_ptr + + Rc::clone(&rc); + Arc::clone(&arc); + RcWeak::clone(&rc_weak); + ArcWeak::clone(&arc_weak); +} + +trait SomeTrait {} +struct SomeImpl; +impl SomeTrait for SomeImpl {} + +fn trait_object() { + let x = Arc::new(SomeImpl); + let _: Arc = x.clone(); + //~^ clone_on_ref_ptr +} + +mod issue2076 { + use std::rc::Rc; + + macro_rules! try_opt { + ($expr: expr) => { + match $expr { + Some(value) => value, + None => return None, + } + }; + } + + fn func() -> Option> { + let rc = Rc::new(42); + Some(try_opt!(Some(rc)).clone()) + //~^ clone_on_ref_ptr + } +} diff --git a/tests/ui/clone_on_ref_ptr.stderr b/tests/ui/clone_on_ref_ptr.stderr new file mode 100644 index 0000000000000..b15f0e803a352 --- /dev/null +++ b/tests/ui/clone_on_ref_ptr.stderr @@ -0,0 +1,41 @@ +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:9:5 + | +LL | rc.clone(); + | ^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&rc)` + | + = note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::clone_on_ref_ptr)]` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:11:5 + | +LL | rc_weak.clone(); + | ^^^^^^^^^^^^^^^ help: try: `std::rc::Weak::::clone(&rc_weak)` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:13:5 + | +LL | arc.clone(); + | ^^^^^^^^^^^ help: try: `std::sync::Arc::::clone(&arc)` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:15:5 + | +LL | arc_weak.clone(); + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::Weak::::clone(&arc_weak)` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:30:33 + | +LL | let _: Arc = x.clone(); + | ^^^^^^^^^ help: try: `std::sync::Arc::::clone(&x)` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:48:14 + | +LL | Some(try_opt!(Some(rc)).clone()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&try_opt!(Some(rc)))` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/collapsible_else_if.fixed b/tests/ui/collapsible_else_if.fixed index 3d709fe9b8e0e..da958f76a5ca0 100644 --- a/tests/ui/collapsible_else_if.fixed +++ b/tests/ui/collapsible_else_if.fixed @@ -87,6 +87,33 @@ fn issue_7318() { //~^^^ collapsible_else_if } +fn issue_13365() { + // all the `expect`s that we should fulfill + if true { + } else { + #[expect(clippy::collapsible_else_if)] + if false {} + } + + if true { + } else { + #[expect(clippy::style)] + if false {} + } + + if true { + } else { + #[expect(clippy::all)] + if false {} + } + + if true { + } else { + #[expect(warnings)] + if false {} + } +} + fn issue14799() { use std::ops::ControlFlow; diff --git a/tests/ui/collapsible_else_if.rs b/tests/ui/collapsible_else_if.rs index 51868e039086f..06af49f2f6f3b 100644 --- a/tests/ui/collapsible_else_if.rs +++ b/tests/ui/collapsible_else_if.rs @@ -103,6 +103,33 @@ fn issue_7318() { //~^^^ collapsible_else_if } +fn issue_13365() { + // all the `expect`s that we should fulfill + if true { + } else { + #[expect(clippy::collapsible_else_if)] + if false {} + } + + if true { + } else { + #[expect(clippy::style)] + if false {} + } + + if true { + } else { + #[expect(clippy::all)] + if false {} + } + + if true { + } else { + #[expect(warnings)] + if false {} + } +} + fn issue14799() { use std::ops::ControlFlow; diff --git a/tests/ui/collapsible_else_if.stderr b/tests/ui/collapsible_else_if.stderr index 1a7bcec7fd5df..ce1da593a8e93 100644 --- a/tests/ui/collapsible_else_if.stderr +++ b/tests/ui/collapsible_else_if.stderr @@ -151,7 +151,7 @@ LL | | } | |_____^ help: collapse nested if block: `if false {}` error: this `else { if .. }` block can be collapsed - --> tests/ui/collapsible_else_if.rs:130:12 + --> tests/ui/collapsible_else_if.rs:157:12 | LL | } else { | ____________^ diff --git a/tests/ui/collapsible_else_if_unfixable.rs b/tests/ui/collapsible_else_if_unfixable.rs new file mode 100644 index 0000000000000..e5c18cf3ba901 --- /dev/null +++ b/tests/ui/collapsible_else_if_unfixable.rs @@ -0,0 +1,22 @@ +//@no-rustfix +#![warn(clippy::collapsible_else_if)] + +fn issue_13365() { + // in the following examples, we won't lint because of the comments, + // so the the `expect` will be unfulfilled + if true { + } else { + // some other text before + #[expect(clippy::collapsible_else_if)] + if false {} + } + //~^^^ ERROR: this lint expectation is unfulfilled + + if true { + } else { + #[expect(clippy::collapsible_else_if)] + // some other text after + if false {} + } + //~^^^^ ERROR: this lint expectation is unfulfilled +} diff --git a/tests/ui/collapsible_else_if_unfixable.stderr b/tests/ui/collapsible_else_if_unfixable.stderr new file mode 100644 index 0000000000000..b461ceba6de7f --- /dev/null +++ b/tests/ui/collapsible_else_if_unfixable.stderr @@ -0,0 +1,17 @@ +error: this lint expectation is unfulfilled + --> tests/ui/collapsible_else_if_unfixable.rs:10:18 + | +LL | #[expect(clippy::collapsible_else_if)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D unfulfilled-lint-expectations` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unfulfilled_lint_expectations)]` + +error: this lint expectation is unfulfilled + --> tests/ui/collapsible_else_if_unfixable.rs:17:18 + | +LL | #[expect(clippy::collapsible_else_if)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed index 78354c2d7cf88..ca9d02ff2d4f8 100644 --- a/tests/ui/collapsible_if.fixed +++ b/tests/ui/collapsible_if.fixed @@ -144,6 +144,24 @@ fn layout_check() -> u32 { //~^^^^^ collapsible_if } +fn issue13365() { + // all the `expect`s that we should fulfill + if true { + #[expect(clippy::collapsible_if)] + if true {} + } + + if true { + #[expect(clippy::style)] + if true {} + } + + if true { + #[expect(clippy::all)] + if true {} + } +} + fn issue14722() { let x = if true { Some(1) diff --git a/tests/ui/collapsible_if.rs b/tests/ui/collapsible_if.rs index 5d9afa109569d..9ac68ecd4cac0 100644 --- a/tests/ui/collapsible_if.rs +++ b/tests/ui/collapsible_if.rs @@ -154,6 +154,24 @@ fn layout_check() -> u32 { //~^^^^^ collapsible_if } +fn issue13365() { + // all the `expect`s that we should fulfill + if true { + #[expect(clippy::collapsible_if)] + if true {} + } + + if true { + #[expect(clippy::style)] + if true {} + } + + if true { + #[expect(clippy::all)] + if true {} + } +} + fn issue14722() { let x = if true { Some(1) diff --git a/tests/ui/collapsible_if.stderr b/tests/ui/collapsible_if.stderr index a685cc2e9291d..b1f26630f529b 100644 --- a/tests/ui/collapsible_if.stderr +++ b/tests/ui/collapsible_if.stderr @@ -191,7 +191,7 @@ LL ~ ; 3 | error: this `if` statement can be collapsed - --> tests/ui/collapsible_if.rs:178:5 + --> tests/ui/collapsible_if.rs:196:5 | LL | / if true { LL | | (if true { diff --git a/tests/ui/collapsible_if_unfixable.rs b/tests/ui/collapsible_if_unfixable.rs new file mode 100644 index 0000000000000..643520ac0f5d9 --- /dev/null +++ b/tests/ui/collapsible_if_unfixable.rs @@ -0,0 +1,20 @@ +//@ no-rustfix +#![warn(clippy::collapsible_if)] + +fn issue13365() { + // in the following examples, we won't lint because of the comments, + // so the the `expect` will be unfulfilled + if true { + // don't collapsible because of this comment + #[expect(clippy::collapsible_if)] + if true {} + } + //~^^^ ERROR: this lint expectation is unfulfilled + + if true { + #[expect(clippy::collapsible_if)] + // don't collapsible because of this comment + if true {} + } + //~^^^^ ERROR: this lint expectation is unfulfilled +} diff --git a/tests/ui/collapsible_if_unfixable.stderr b/tests/ui/collapsible_if_unfixable.stderr new file mode 100644 index 0000000000000..64c8fb8da2b44 --- /dev/null +++ b/tests/ui/collapsible_if_unfixable.stderr @@ -0,0 +1,17 @@ +error: this lint expectation is unfulfilled + --> tests/ui/collapsible_if_unfixable.rs:9:18 + | +LL | #[expect(clippy::collapsible_if)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D unfulfilled-lint-expectations` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unfulfilled_lint_expectations)]` + +error: this lint expectation is unfulfilled + --> tests/ui/collapsible_if_unfixable.rs:15:18 + | +LL | #[expect(clippy::collapsible_if)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/const_comparisons.rs b/tests/ui/const_comparisons.rs index b732d7d142fc5..a2a1f0a7b4c09 100644 --- a/tests/ui/const_comparisons.rs +++ b/tests/ui/const_comparisons.rs @@ -1,9 +1,11 @@ -#![allow(unused)] -#![warn(clippy::impossible_comparisons)] -#![warn(clippy::redundant_comparisons)] -#![allow(clippy::no_effect)] -#![allow(clippy::short_circuit_statement)] -#![allow(clippy::manual_range_contains)] +#![allow( + unused, + clippy::identity_op, + clippy::manual_range_contains, + clippy::no_effect, + clippy::short_circuit_statement +)] +#![warn(clippy::impossible_comparisons, clippy::redundant_comparisons)] const STATUS_BAD_REQUEST: u16 = 400; const STATUS_SERVER_ERROR: u16 = 500; diff --git a/tests/ui/const_comparisons.stderr b/tests/ui/const_comparisons.stderr index 48a2c6e8d4879..1ce62c23ff293 100644 --- a/tests/ui/const_comparisons.stderr +++ b/tests/ui/const_comparisons.stderr @@ -1,5 +1,5 @@ error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:45:5 + --> tests/ui/const_comparisons.rs:47:5 | LL | status_code <= 400 && status_code > 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | status_code <= 400 && status_code > 500; = help: to override `-D warnings` add `#[allow(clippy::impossible_comparisons)]` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:48:5 + --> tests/ui/const_comparisons.rs:50:5 | LL | status_code > 500 && status_code < 400; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | status_code > 500 && status_code < 400; = note: since `500` > `400`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:51:5 + --> tests/ui/const_comparisons.rs:53:5 | LL | status_code < 500 && status_code > 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | status_code < 500 && status_code > 500; = note: `status_code` cannot simultaneously be greater than and less than `500` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:55:5 + --> tests/ui/const_comparisons.rs:57:5 | LL | status_code < { 400 } && status_code > { 500 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | status_code < { 400 } && status_code > { 500 }; = note: since `{ 400 }` < `{ 500 }`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:58:5 + --> tests/ui/const_comparisons.rs:60:5 | LL | status_code < STATUS_BAD_REQUEST && status_code > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | status_code < STATUS_BAD_REQUEST && status_code > STATUS_SERVER_ERROR; = note: since `STATUS_BAD_REQUEST` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:61:5 + --> tests/ui/const_comparisons.rs:63:5 | LL | status_code <= u16::MIN + 1 && status_code > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | status_code <= u16::MIN + 1 && status_code > STATUS_SERVER_ERROR; = note: since `u16::MIN + 1` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:64:5 + --> tests/ui/const_comparisons.rs:66:5 | LL | status_code < STATUS_SERVER_ERROR && status_code > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -57,7 +57,7 @@ LL | status_code < STATUS_SERVER_ERROR && status_code > STATUS_SERVER_ERROR; = note: `status_code` cannot simultaneously be greater than and less than `STATUS_SERVER_ERROR` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:68:5 + --> tests/ui/const_comparisons.rs:70:5 | LL | status < { 400 } && status > { 500 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | status < { 400 } && status > { 500 }; = note: since `{ 400 }` < `{ 500 }`, the expression evaluates to false for any value of `status` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:71:5 + --> tests/ui/const_comparisons.rs:73:5 | LL | status < STATUS_BAD_REQUEST && status > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | status < STATUS_BAD_REQUEST && status > STATUS_SERVER_ERROR; = note: since `STATUS_BAD_REQUEST` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:74:5 + --> tests/ui/const_comparisons.rs:76:5 | LL | status <= u16::MIN + 1 && status > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -81,7 +81,7 @@ LL | status <= u16::MIN + 1 && status > STATUS_SERVER_ERROR; = note: since `u16::MIN + 1` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:77:5 + --> tests/ui/const_comparisons.rs:79:5 | LL | status < STATUS_SERVER_ERROR && status > STATUS_SERVER_ERROR; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -89,7 +89,7 @@ LL | status < STATUS_SERVER_ERROR && status > STATUS_SERVER_ERROR; = note: `status` cannot simultaneously be greater than and less than `STATUS_SERVER_ERROR` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:86:5 + --> tests/ui/const_comparisons.rs:88:5 | LL | 500 >= status_code && 600 < status_code; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL | 500 >= status_code && 600 < status_code; = note: since `500` < `600`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:90:5 + --> tests/ui/const_comparisons.rs:92:5 | LL | 500 >= status_code && status_code > 600; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -105,7 +105,7 @@ LL | 500 >= status_code && status_code > 600; = note: since `500` < `600`, the expression evaluates to false for any value of `status_code` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:99:5 + --> tests/ui/const_comparisons.rs:101:5 | LL | 500 >= status && 600 < status; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -113,7 +113,7 @@ LL | 500 >= status && 600 < status; = note: since `500` < `600`, the expression evaluates to false for any value of `status` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:103:5 + --> tests/ui/const_comparisons.rs:105:5 | LL | 500 >= status && status > 600; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -121,13 +121,13 @@ LL | 500 >= status && status > 600; = note: since `500` < `600`, the expression evaluates to false for any value of `status` error: right-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:107:5 + --> tests/ui/const_comparisons.rs:109:5 | LL | status_code < 200 && status_code <= 299; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code < 200` evaluates to true, status_code <= 299` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:107:23 + --> tests/ui/const_comparisons.rs:109:23 | LL | status_code < 200 && status_code <= 299; | ^^^^^^^^^^^^^^^^^^^^^ @@ -135,67 +135,67 @@ LL | status_code < 200 && status_code <= 299; = help: to override `-D warnings` add `#[allow(clippy::redundant_comparisons)]` error: left-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:110:5 + --> tests/ui/const_comparisons.rs:112:5 | LL | status_code > 200 && status_code >= 299; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code >= 299` evaluates to true, status_code > 200` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:110:5 + --> tests/ui/const_comparisons.rs:112:5 | LL | status_code > 200 && status_code >= 299; | ^^^^^^^^^^^^^^^^^^^^^ error: left-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:114:5 + --> tests/ui/const_comparisons.rs:116:5 | LL | status_code >= 500 && status_code > 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code > 500` evaluates to true, status_code >= 500` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:114:5 + --> tests/ui/const_comparisons.rs:116:5 | LL | status_code >= 500 && status_code > 500; | ^^^^^^^^^^^^^^^^^^^^^^ error: right-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:118:5 + --> tests/ui/const_comparisons.rs:120:5 | LL | status_code > 500 && status_code >= 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code > 500` evaluates to true, status_code >= 500` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:118:23 + --> tests/ui/const_comparisons.rs:120:23 | LL | status_code > 500 && status_code >= 500; | ^^^^^^^^^^^^^^^^^^^^^ error: left-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:122:5 + --> tests/ui/const_comparisons.rs:124:5 | LL | status_code <= 500 && status_code < 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code < 500` evaluates to true, status_code <= 500` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:122:5 + --> tests/ui/const_comparisons.rs:124:5 | LL | status_code <= 500 && status_code < 500; | ^^^^^^^^^^^^^^^^^^^^^^ error: right-hand side of `&&` operator has no effect - --> tests/ui/const_comparisons.rs:126:5 + --> tests/ui/const_comparisons.rs:128:5 | LL | status_code < 500 && status_code <= 500; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `if `status_code < 500` evaluates to true, status_code <= 500` will always evaluate to true as well - --> tests/ui/const_comparisons.rs:126:23 + --> tests/ui/const_comparisons.rs:128:23 | LL | status_code < 500 && status_code <= 500; | ^^^^^^^^^^^^^^^^^^^^^ error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:131:5 + --> tests/ui/const_comparisons.rs:133:5 | LL | name < "Jennifer" && name > "Shannon"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -203,7 +203,7 @@ LL | name < "Jennifer" && name > "Shannon"; = note: since `"Jennifer"` < `"Shannon"`, the expression evaluates to false for any value of `name` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:135:5 + --> tests/ui/const_comparisons.rs:137:5 | LL | numbers < [3, 4] && numbers > [5, 6]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -211,7 +211,7 @@ LL | numbers < [3, 4] && numbers > [5, 6]; = note: since `[3, 4]` < `[5, 6]`, the expression evaluates to false for any value of `numbers` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:139:5 + --> tests/ui/const_comparisons.rs:141:5 | LL | letter < 'b' && letter > 'c'; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -219,7 +219,7 @@ LL | letter < 'b' && letter > 'c'; = note: since `'b'` < `'c'`, the expression evaluates to false for any value of `letter` error: boolean expression will never evaluate to 'true' - --> tests/ui/const_comparisons.rs:143:5 + --> tests/ui/const_comparisons.rs:145:5 | LL | area < std::f32::consts::E && area > std::f32::consts::PI; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/const_is_empty.rs b/tests/ui/const_is_empty.rs index 8bb4f0e5d9750..2ad1b5276def8 100644 --- a/tests/ui/const_is_empty.rs +++ b/tests/ui/const_is_empty.rs @@ -55,53 +55,6 @@ 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(); - //~^ const_is_empty - - let _ = NON_EMPTY_STR.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_BSTR.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_BSTR.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_ARRAY.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_ARRAY_REPEAT.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_U8_SLICE.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_U8_SLICE.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_ARRAY.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_ARRAY_REPEAT.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_REF_ARRAY.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_REF_ARRAY.is_empty(); - //~^ const_is_empty - - let _ = EMPTY_SLICE.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_SLICE.is_empty(); - //~^ const_is_empty - - let _ = NON_EMPTY_SLICE_REPEAT.is_empty(); - //~^ const_is_empty -} - fn main() { let value = "foobar"; let _ = value.is_empty(); @@ -120,7 +73,7 @@ fn main() { fn str_from_arg(var: &str) { var.is_empty(); - // Do not lint, we know nothiny about var + // Do not lint, we know nothing about var } fn update_str() { @@ -200,6 +153,5 @@ fn issue_13106() { const { EMPTY_STR.is_empty(); - //~^ const_is_empty } } diff --git a/tests/ui/const_is_empty.stderr b/tests/ui/const_is_empty.stderr index 2ba189058e832..e1837695bc1c9 100644 --- a/tests/ui/const_is_empty.stderr +++ b/tests/ui/const_is_empty.stderr @@ -37,131 +37,35 @@ error: this expression always evaluates to false LL | if non_empty2.is_empty() { | ^^^^^^^^^^^^^^^^^^^^^ -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:59:13 - | -LL | let _ = EMPTY_STR.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> 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:65:13 - | -LL | let _ = EMPTY_BSTR.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:68:13 - | -LL | let _ = NON_EMPTY_BSTR.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:71:13 - | -LL | let _ = EMPTY_ARRAY.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:74:13 - | -LL | let _ = EMPTY_ARRAY_REPEAT.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:77:13 - | -LL | let _ = EMPTY_U8_SLICE.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:80:13 - | -LL | let _ = NON_EMPTY_U8_SLICE.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:83:13 - | -LL | let _ = NON_EMPTY_ARRAY.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:86:13 - | -LL | let _ = NON_EMPTY_ARRAY_REPEAT.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:89:13 - | -LL | let _ = EMPTY_REF_ARRAY.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:92:13 - | -LL | let _ = NON_EMPTY_REF_ARRAY.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:95:13 - | -LL | let _ = EMPTY_SLICE.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:98:13 - | -LL | let _ = NON_EMPTY_SLICE.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:101:13 - | -LL | let _ = NON_EMPTY_SLICE_REPEAT.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:107:13 + --> tests/ui/const_is_empty.rs:60:13 | LL | let _ = value.is_empty(); | ^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:111:13 + --> tests/ui/const_is_empty.rs:64:13 | LL | let _ = x.is_empty(); | ^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:114:13 + --> tests/ui/const_is_empty.rs:67:13 | LL | let _ = "".is_empty(); | ^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:117:13 + --> tests/ui/const_is_empty.rs:70:13 | LL | let _ = b"".is_empty(); | ^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:171:13 + --> tests/ui/const_is_empty.rs:124:13 | LL | let _ = val.is_empty(); | ^^^^^^^^^^^^^^ -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:202:9 - | -LL | EMPTY_STR.is_empty(); - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 27 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/crashes/ice-4775.rs b/tests/ui/crashes/ice-4775.rs index dd6c6b8de25af..e8c47b4f3a77b 100644 --- a/tests/ui/crashes/ice-4775.rs +++ b/tests/ui/crashes/ice-4775.rs @@ -7,7 +7,7 @@ pub struct ArrayWrapper([usize; N]); impl ArrayWrapper<{ N }> { pub fn ice(&self) { for i in self.0.iter() { - println!("{}", i); + println!("{i}"); } } } diff --git a/tests/ui/default_trait_access.fixed b/tests/ui/default_trait_access.fixed index d3fe09a052ef3..e3bf603da80f3 100644 --- a/tests/ui/default_trait_access.fixed +++ b/tests/ui/default_trait_access.fixed @@ -1,7 +1,6 @@ //@aux-build: proc_macros.rs #![deny(clippy::default_trait_access)] #![allow(dead_code, unused_imports)] -#![allow(clippy::uninlined_format_args)] extern crate proc_macros; @@ -63,6 +62,7 @@ fn main() { let _s21: String = with_span!(s Default::default()); + #[expect(clippy::uninlined_format_args)] println!( "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}]", s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, diff --git a/tests/ui/default_trait_access.rs b/tests/ui/default_trait_access.rs index cdffb2a2ee8ca..8cc065e5bced2 100644 --- a/tests/ui/default_trait_access.rs +++ b/tests/ui/default_trait_access.rs @@ -1,7 +1,6 @@ //@aux-build: proc_macros.rs #![deny(clippy::default_trait_access)] #![allow(dead_code, unused_imports)] -#![allow(clippy::uninlined_format_args)] extern crate proc_macros; @@ -63,6 +62,7 @@ fn main() { let _s21: String = with_span!(s Default::default()); + #[expect(clippy::uninlined_format_args)] println!( "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}]", s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, diff --git a/tests/ui/default_trait_access.stderr b/tests/ui/default_trait_access.stderr index aa7eb4f89558c..aa89516f175c4 100644 --- a/tests/ui/default_trait_access.stderr +++ b/tests/ui/default_trait_access.stderr @@ -1,5 +1,5 @@ error: calling `String::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:13:22 + --> tests/ui/default_trait_access.rs:12:22 | LL | let s1: String = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `String::default()` @@ -11,43 +11,43 @@ LL | #![deny(clippy::default_trait_access)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: calling `String::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:18:22 + --> tests/ui/default_trait_access.rs:17:22 | LL | let s3: String = D2::default(); | ^^^^^^^^^^^^^ help: try: `String::default()` error: calling `String::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:21:22 + --> tests/ui/default_trait_access.rs:20:22 | LL | let s4: String = std::default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `String::default()` error: calling `String::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:26:22 + --> tests/ui/default_trait_access.rs:25:22 | LL | let s6: String = default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `String::default()` error: calling `GenericDerivedDefault::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:37:46 + --> tests/ui/default_trait_access.rs:36:46 | LL | let s11: GenericDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()` error: calling `TupleDerivedDefault::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:44:36 + --> tests/ui/default_trait_access.rs:43:36 | LL | let s14: TupleDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()` error: calling `ArrayDerivedDefault::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:47:36 + --> tests/ui/default_trait_access.rs:46:36 | LL | let s15: ArrayDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()` error: calling `TupleStructDerivedDefault::default()` is more clear than this expression - --> tests/ui/default_trait_access.rs:52:42 + --> tests/ui/default_trait_access.rs:51:42 | LL | let s17: TupleStructDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleStructDerivedDefault::default()` diff --git a/tests/ui/derive.rs b/tests/ui/derive.rs index e334203c7b2a7..b8adb7601db64 100644 --- a/tests/ui/derive.rs +++ b/tests/ui/derive.rs @@ -131,3 +131,16 @@ fn issue14558() { } fn main() {} + +mod issue15708 { + // Check that the lint posts on the type definition node + #[expect(clippy::expl_impl_clone_on_copy)] + #[derive(Copy)] + struct S; + + impl Clone for S { + fn clone(&self) -> Self { + S + } + } +} diff --git a/tests/ui/derive.stderr b/tests/ui/derive.stderr index 9004ced6849e5..7dbc38a2f621f 100644 --- a/tests/ui/derive.stderr +++ b/tests/ui/derive.stderr @@ -9,7 +9,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -note: consider deriving `Clone` or removing `Copy` +help: consider deriving `Clone` or removing `Copy` --> tests/ui/derive.rs:16:1 | LL | / impl Clone for Qux { @@ -33,7 +33,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -note: consider deriving `Clone` or removing `Copy` +help: consider deriving `Clone` or removing `Copy` --> tests/ui/derive.rs:42:1 | LL | / impl<'a> Clone for Lt<'a> { @@ -55,7 +55,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -note: consider deriving `Clone` or removing `Copy` +help: consider deriving `Clone` or removing `Copy` --> tests/ui/derive.rs:55:1 | LL | / impl Clone for BigArray { @@ -77,7 +77,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -note: consider deriving `Clone` or removing `Copy` +help: consider deriving `Clone` or removing `Copy` --> tests/ui/derive.rs:68:1 | LL | / impl Clone for FnPtr { @@ -99,7 +99,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -note: consider deriving `Clone` or removing `Copy` +help: consider deriving `Clone` or removing `Copy` --> tests/ui/derive.rs:90:1 | LL | / impl Clone for Generic2 { diff --git a/tests/ui/derive_ord_xor_partial_ord.rs b/tests/ui/derive_ord_xor_partial_ord.rs index 3ef4ee9463dca..b4bb24b0d2fe7 100644 --- a/tests/ui/derive_ord_xor_partial_ord.rs +++ b/tests/ui/derive_ord_xor_partial_ord.rs @@ -76,3 +76,18 @@ mod use_ord { } fn main() {} + +mod issue15708 { + use std::cmp::{Ord, Ordering}; + + // Check that the lint posts on the type definition node + #[expect(clippy::derive_ord_xor_partial_ord)] + #[derive(PartialOrd, PartialEq, Eq)] + struct DerivePartialOrdInUseOrd; + + impl Ord for DerivePartialOrdInUseOrd { + fn cmp(&self, other: &Self) -> Ordering { + Ordering::Less + } + } +} diff --git a/tests/ui/derived_hash_with_manual_eq.rs b/tests/ui/derived_hash_with_manual_eq.rs index 88b574add3f27..9f5c85d7fbc38 100644 --- a/tests/ui/derived_hash_with_manual_eq.rs +++ b/tests/ui/derived_hash_with_manual_eq.rs @@ -41,3 +41,19 @@ impl std::hash::Hash for Bah { } fn main() {} + +mod issue15708 { + // Check that the lint posts on the type definition node + #[expect(clippy::derived_hash_with_manual_eq)] + #[derive(Debug, Clone, Copy, Eq, PartialOrd, Ord, Hash)] + pub struct Span { + start: usize, + end: usize, + } + + impl PartialEq for Span { + fn eq(&self, other: &Self) -> bool { + self.start.cmp(&other.start).then(self.end.cmp(&other.end)).is_eq() + } + } +} diff --git a/tests/ui/double_parens.fixed b/tests/ui/double_parens.fixed new file mode 100644 index 0000000000000..dedc513438d11 --- /dev/null +++ b/tests/ui/double_parens.fixed @@ -0,0 +1,99 @@ +#![warn(clippy::double_parens)] +#![expect(clippy::eq_op, clippy::no_effect)] +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +fn dummy_fn(_: T) {} + +struct DummyStruct; + +impl DummyStruct { + fn dummy_method(&self, _: T) {} +} + +fn simple_double_parens() -> i32 { + (0) + //~^ double_parens +} + +fn fn_double_parens() { + dummy_fn(0); + //~^ double_parens +} + +fn method_double_parens(x: DummyStruct) { + x.dummy_method(0); + //~^ double_parens +} + +fn tuple_double_parens() -> (i32, i32) { + (1, 2) + //~^ double_parens +} + +#[allow(clippy::unused_unit)] +fn unit_double_parens() { + () + //~^ double_parens +} + +fn fn_tuple_ok() { + dummy_fn((1, 2)); +} + +fn method_tuple_ok(x: DummyStruct) { + x.dummy_method((1, 2)); +} + +fn fn_unit_ok() { + dummy_fn(()); +} + +fn method_unit_ok(x: DummyStruct) { + x.dummy_method(()); +} + +// Issue #3206 +fn inside_macro() { + assert_eq!((1, 2), (1, 2), "Error"); + assert_eq!((1, 2), (1, 2), "Error"); + //~^ double_parens +} + +fn issue9000(x: DummyStruct) { + macro_rules! foo { + () => {(100)} + } + // don't lint: the inner paren comes from the macro expansion + (foo!()); + dummy_fn(foo!()); + x.dummy_method(foo!()); + + macro_rules! baz { + ($n:literal) => {($n)} + } + // don't lint: don't get confused by the expression inside the inner paren + // having the same `ctxt` as the overall expression + // (this is a bug that happened during the development of the fix) + (baz!(100)); + dummy_fn(baz!(100)); + x.dummy_method(baz!(100)); + + // should lint: both parens are from inside the macro + macro_rules! bar { + () => {(100)} + //~^ double_parens + } + bar!(); + + // should lint: both parens are from outside the macro; + // make sure to suggest the macro unexpanded + (vec![1, 2]); + //~^ double_parens + dummy_fn(vec![1, 2]); + //~^ double_parens + x.dummy_method(vec![1, 2]); + //~^ double_parens +} + +fn main() {} diff --git a/tests/ui/double_parens.rs b/tests/ui/double_parens.rs index 7c976015b4e79..27f252485b714 100644 --- a/tests/ui/double_parens.rs +++ b/tests/ui/double_parens.rs @@ -1,5 +1,5 @@ #![warn(clippy::double_parens)] -#![allow(dead_code, clippy::eq_op)] +#![expect(clippy::eq_op, clippy::no_effect)] #![feature(custom_inner_attributes)] #![rustfmt::skip] @@ -8,38 +8,33 @@ fn dummy_fn(_: T) {} struct DummyStruct; impl DummyStruct { - fn dummy_method(self, _: T) {} + fn dummy_method(&self, _: T) {} } fn simple_double_parens() -> i32 { ((0)) //~^ double_parens - - } fn fn_double_parens() { dummy_fn((0)); //~^ double_parens - } fn method_double_parens(x: DummyStruct) { x.dummy_method((0)); //~^ double_parens - } fn tuple_double_parens() -> (i32, i32) { ((1, 2)) //~^ double_parens - } +#[allow(clippy::unused_unit)] fn unit_double_parens() { (()) //~^ double_parens - } fn fn_tuple_ok() { @@ -63,7 +58,42 @@ fn inside_macro() { assert_eq!((1, 2), (1, 2), "Error"); assert_eq!(((1, 2)), (1, 2), "Error"); //~^ double_parens +} +fn issue9000(x: DummyStruct) { + macro_rules! foo { + () => {(100)} + } + // don't lint: the inner paren comes from the macro expansion + (foo!()); + dummy_fn(foo!()); + x.dummy_method(foo!()); + + macro_rules! baz { + ($n:literal) => {($n)} + } + // don't lint: don't get confused by the expression inside the inner paren + // having the same `ctxt` as the overall expression + // (this is a bug that happened during the development of the fix) + (baz!(100)); + dummy_fn(baz!(100)); + x.dummy_method(baz!(100)); + + // should lint: both parens are from inside the macro + macro_rules! bar { + () => {((100))} + //~^ double_parens + } + bar!(); + + // should lint: both parens are from outside the macro; + // make sure to suggest the macro unexpanded + ((vec![1, 2])); + //~^ double_parens + dummy_fn((vec![1, 2])); + //~^ double_parens + x.dummy_method((vec![1, 2])); + //~^ double_parens } fn main() {} diff --git a/tests/ui/double_parens.stderr b/tests/ui/double_parens.stderr index e119f54949b1f..3a740e44cacf3 100644 --- a/tests/ui/double_parens.stderr +++ b/tests/ui/double_parens.stderr @@ -1,41 +1,70 @@ -error: consider removing unnecessary double parentheses +error: unnecessary parentheses --> tests/ui/double_parens.rs:15:5 | LL | ((0)) - | ^^^^^ + | ^^^^^ help: remove them: `(0)` | = note: `-D clippy::double-parens` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::double_parens)]` -error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:22:14 +error: unnecessary parentheses + --> tests/ui/double_parens.rs:20:14 | LL | dummy_fn((0)); - | ^^^ + | ^^^ help: remove them: `0` -error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:28:20 +error: unnecessary parentheses + --> tests/ui/double_parens.rs:25:20 | LL | x.dummy_method((0)); - | ^^^ + | ^^^ help: remove them: `0` -error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:34:5 +error: unnecessary parentheses + --> tests/ui/double_parens.rs:30:5 | LL | ((1, 2)) - | ^^^^^^^^ + | ^^^^^^^^ help: remove them: `(1, 2)` -error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:40:5 +error: unnecessary parentheses + --> tests/ui/double_parens.rs:36:5 | LL | (()) - | ^^^^ + | ^^^^ help: remove them: `()` -error: consider removing unnecessary double parentheses - --> tests/ui/double_parens.rs:64:16 +error: unnecessary parentheses + --> tests/ui/double_parens.rs:59:16 | LL | assert_eq!(((1, 2)), (1, 2), "Error"); - | ^^^^^^^^ + | ^^^^^^^^ help: remove them: `(1, 2)` -error: aborting due to 6 previous errors +error: unnecessary parentheses + --> tests/ui/double_parens.rs:84:16 + | +LL | () => {((100))} + | ^^^^^^^ help: remove them: `(100)` +... +LL | bar!(); + | ------ in this macro invocation + | + = note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unnecessary parentheses + --> tests/ui/double_parens.rs:91:5 + | +LL | ((vec![1, 2])); + | ^^^^^^^^^^^^^^ help: remove them: `(vec![1, 2])` + +error: unnecessary parentheses + --> tests/ui/double_parens.rs:93:14 + | +LL | dummy_fn((vec![1, 2])); + | ^^^^^^^^^^^^ help: remove them: `vec![1, 2]` + +error: unnecessary parentheses + --> tests/ui/double_parens.rs:95:20 + | +LL | x.dummy_method((vec![1, 2])); + | ^^^^^^^^^^^^ help: remove them: `vec![1, 2]` + +error: aborting due to 10 previous errors diff --git a/tests/ui/duration_subsec.fixed b/tests/ui/duration_subsec.fixed index a8c2f78ca383a..b6b2d156c0e0f 100644 --- a/tests/ui/duration_subsec.fixed +++ b/tests/ui/duration_subsec.fixed @@ -25,8 +25,7 @@ fn main() { // Handle constants const NANOS_IN_MICRO: u32 = 1_000; - let _ = dur.subsec_micros(); - //~^ duration_subsec + let _ = dur.subsec_nanos() / NANOS_IN_MICRO; // Other literals aren't linted let _ = dur.subsec_nanos() / 699; diff --git a/tests/ui/duration_subsec.rs b/tests/ui/duration_subsec.rs index 582f4717de27f..1061e6003c35e 100644 --- a/tests/ui/duration_subsec.rs +++ b/tests/ui/duration_subsec.rs @@ -26,7 +26,6 @@ fn main() { // Handle constants const NANOS_IN_MICRO: u32 = 1_000; let _ = dur.subsec_nanos() / NANOS_IN_MICRO; - //~^ duration_subsec // Other literals aren't linted let _ = dur.subsec_nanos() / 699; diff --git a/tests/ui/duration_subsec.stderr b/tests/ui/duration_subsec.stderr index 1a41742e1fa6d..27756bc1c20f6 100644 --- a/tests/ui/duration_subsec.stderr +++ b/tests/ui/duration_subsec.stderr @@ -25,11 +25,5 @@ error: calling `subsec_micros()` is more concise than this calculation LL | let _ = (&dur).subsec_nanos() / 1_000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&dur).subsec_micros()` -error: calling `subsec_micros()` is more concise than this calculation - --> tests/ui/duration_subsec.rs:28:13 - | -LL | let _ = dur.subsec_nanos() / NANOS_IN_MICRO; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()` - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/explicit_counter_loop.rs b/tests/ui/explicit_counter_loop.rs index 13934785d7b15..ec4cecf377667 100644 --- a/tests/ui/explicit_counter_loop.rs +++ b/tests/ui/explicit_counter_loop.rs @@ -1,5 +1,5 @@ #![warn(clippy::explicit_counter_loop)] -#![allow(clippy::uninlined_format_args, clippy::useless_vec)] +#![allow(clippy::useless_vec)] //@no-rustfix: suggestion does not remove the `+= 1` fn main() { let mut vec = vec![1, 2, 3, 4]; @@ -89,13 +89,13 @@ mod issue_1219 { for _v in &vec { index += 1 } - println!("index: {}", index); + println!("index: {index}"); // should not trigger the lint because the count is conditional #1219 let text = "banana"; let mut count = 0; for ch in text.chars() { - println!("{}", count); + println!("{count}"); if ch == 'a' { continue; } @@ -106,7 +106,7 @@ mod issue_1219 { let text = "banana"; let mut count = 0; for ch in text.chars() { - println!("{}", count); + println!("{count}"); if ch == 'a' { count += 1; } @@ -118,7 +118,7 @@ mod issue_1219 { for ch in text.chars() { //~^ explicit_counter_loop - println!("{}", count); + println!("{count}"); count += 1; if ch == 'a' { continue; @@ -131,7 +131,7 @@ mod issue_1219 { for ch in text.chars() { //~^ explicit_counter_loop - println!("{}", count); + println!("{count}"); count += 1; for i in 0..2 { let _ = 123; @@ -142,7 +142,7 @@ mod issue_1219 { let text = "banana"; let mut count = 0; for ch in text.chars() { - println!("{}", count); + println!("{count}"); count += 1; for i in 0..2 { count += 1; @@ -157,7 +157,7 @@ mod issue_3308 { let mut skips = 0; let erasures = vec![]; for i in 0..10 { - println!("{}", skips); + println!("{skips}"); while erasures.contains(&(i + skips)) { skips += 1; } @@ -166,7 +166,7 @@ mod issue_3308 { // should not trigger the lint because the count is incremented multiple times let mut skips = 0; for i in 0..10 { - println!("{}", skips); + println!("{skips}"); let mut j = 0; while j < 5 { skips += 1; @@ -177,7 +177,7 @@ mod issue_3308 { // should not trigger the lint because the count is incremented multiple times let mut skips = 0; for i in 0..10 { - println!("{}", skips); + println!("{skips}"); for j in 0..5 { skips += 1; } @@ -205,7 +205,7 @@ mod issue_4732 { for _v in slice { index += 1 } - let _closure = || println!("index: {}", index); + let _closure = || println!("index: {index}"); } } @@ -217,7 +217,7 @@ mod issue_4677 { let mut count = 0; for _i in slice { count += 1; - println!("{}", count); + println!("{count}"); } } } diff --git a/tests/ui/explicit_deref_methods.fixed b/tests/ui/explicit_deref_methods.fixed index 52c4d1b1f301a..97e8e0bafe4fd 100644 --- a/tests/ui/explicit_deref_methods.fixed +++ b/tests/ui/explicit_deref_methods.fixed @@ -1,3 +1,4 @@ +//@aux-build:proc_macros.rs #![warn(clippy::explicit_deref_methods)] #![allow(unused_variables, unused_must_use)] #![allow( @@ -14,6 +15,8 @@ use std::ops::{Deref, DerefMut}; +extern crate proc_macros; + fn concat(deref_str: &str) -> String { format!("{}bar", deref_str) } @@ -121,6 +124,18 @@ fn main() { let b: &str = expr_deref!(&*a); //~^ explicit_deref_methods + proc_macros::external! { + let a: &mut String = &mut String::from("foo"); + let b: &str = a.deref(); + } + + // Issue #15168 + proc_macros::with_span! { + span + let a: &mut String = &mut String::from("foo"); + let b: &str = a.deref(); + } + // The struct does not implement Deref trait #[derive(Copy, Clone)] struct NoLint(u32); diff --git a/tests/ui/explicit_deref_methods.rs b/tests/ui/explicit_deref_methods.rs index 706d6cb2b79a6..b689649d49dd7 100644 --- a/tests/ui/explicit_deref_methods.rs +++ b/tests/ui/explicit_deref_methods.rs @@ -1,3 +1,4 @@ +//@aux-build:proc_macros.rs #![warn(clippy::explicit_deref_methods)] #![allow(unused_variables, unused_must_use)] #![allow( @@ -14,6 +15,8 @@ use std::ops::{Deref, DerefMut}; +extern crate proc_macros; + fn concat(deref_str: &str) -> String { format!("{}bar", deref_str) } @@ -121,6 +124,18 @@ fn main() { let b: &str = expr_deref!(a.deref()); //~^ explicit_deref_methods + proc_macros::external! { + let a: &mut String = &mut String::from("foo"); + let b: &str = a.deref(); + } + + // Issue #15168 + proc_macros::with_span! { + span + let a: &mut String = &mut String::from("foo"); + let b: &str = a.deref(); + } + // The struct does not implement Deref trait #[derive(Copy, Clone)] struct NoLint(u32); diff --git a/tests/ui/explicit_deref_methods.stderr b/tests/ui/explicit_deref_methods.stderr index 5036884366cfc..e2f2e68720b17 100644 --- a/tests/ui/explicit_deref_methods.stderr +++ b/tests/ui/explicit_deref_methods.stderr @@ -1,5 +1,5 @@ error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:55:19 + --> tests/ui/explicit_deref_methods.rs:58:19 | LL | let b: &str = a.deref(); | ^^^^^^^^^ help: try: `&*a` @@ -8,73 +8,73 @@ LL | let b: &str = a.deref(); = help: to override `-D warnings` add `#[allow(clippy::explicit_deref_methods)]` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:58:23 + --> tests/ui/explicit_deref_methods.rs:61:23 | LL | let b: &mut str = a.deref_mut(); | ^^^^^^^^^^^^^ help: try: `&mut **a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:62:39 + --> tests/ui/explicit_deref_methods.rs:65:39 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:62:50 + --> tests/ui/explicit_deref_methods.rs:65:50 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:66:20 + --> tests/ui/explicit_deref_methods.rs:69:20 | LL | println!("{}", a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:70:11 + --> tests/ui/explicit_deref_methods.rs:73:11 | LL | match a.deref() { | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:75:28 + --> tests/ui/explicit_deref_methods.rs:78:28 | LL | let b: String = concat(a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:78:13 + --> tests/ui/explicit_deref_methods.rs:81:13 | LL | let b = just_return(a).deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:81:28 + --> tests/ui/explicit_deref_methods.rs:84:28 | LL | let b: String = concat(just_return(a).deref()); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:121:31 + --> tests/ui/explicit_deref_methods.rs:124:31 | LL | let b: &str = expr_deref!(a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:139:14 + --> tests/ui/explicit_deref_methods.rs:154:14 | LL | let _ = &Deref::deref(&"foo"); | ^^^^^^^^^^^^^^^^^^^^ help: try: `*&"foo"` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:141:14 + --> tests/ui/explicit_deref_methods.rs:156:14 | LL | let _ = &DerefMut::deref_mut(&mut x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut **&mut x` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:142:14 + --> tests/ui/explicit_deref_methods.rs:157:14 | LL | let _ = &DerefMut::deref_mut((&mut &mut x).deref_mut()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut ***(&mut &mut x)` diff --git a/tests/ui/explicit_write.fixed b/tests/ui/explicit_write.fixed index 024999fc609ee..ab28c1ccd8e0d 100644 --- a/tests/ui/explicit_write.fixed +++ b/tests/ui/explicit_write.fixed @@ -1,6 +1,5 @@ #![warn(clippy::explicit_write)] #![allow(unused_imports)] -#![allow(clippy::uninlined_format_args)] fn stdout() -> String { String::new() @@ -40,7 +39,7 @@ fn main() { //~^ explicit_write let value = 1; - eprintln!("with {}", value); + eprintln!("with {value}"); //~^ explicit_write eprintln!("with {} {}", 2, value); //~^ explicit_write @@ -49,7 +48,7 @@ fn main() { eprintln!("macro arg {}", one!()); //~^ explicit_write let width = 2; - eprintln!("{:w$}", value, w = width); + eprintln!("{value:w$}", w = width); //~^ explicit_write } // these should not warn, different destination diff --git a/tests/ui/explicit_write.rs b/tests/ui/explicit_write.rs index c83c760d48c8b..975ee103b6278 100644 --- a/tests/ui/explicit_write.rs +++ b/tests/ui/explicit_write.rs @@ -1,6 +1,5 @@ #![warn(clippy::explicit_write)] #![allow(unused_imports)] -#![allow(clippy::uninlined_format_args)] fn stdout() -> String { String::new() @@ -40,7 +39,7 @@ fn main() { //~^ explicit_write let value = 1; - writeln!(std::io::stderr(), "with {}", value).unwrap(); + writeln!(std::io::stderr(), "with {value}").unwrap(); //~^ explicit_write writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap(); //~^ explicit_write @@ -49,7 +48,7 @@ fn main() { writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap(); //~^ explicit_write let width = 2; - writeln!(std::io::stderr(), "{:w$}", value, w = width).unwrap(); + writeln!(std::io::stderr(), "{value:w$}", w = width).unwrap(); //~^ explicit_write } // these should not warn, different destination diff --git a/tests/ui/explicit_write.stderr b/tests/ui/explicit_write.stderr index 670a0411b3107..ef4b2a049a6d9 100644 --- a/tests/ui/explicit_write.stderr +++ b/tests/ui/explicit_write.stderr @@ -1,5 +1,5 @@ error: use of `write!(stdout(), ...).unwrap()` - --> tests/ui/explicit_write.rs:23:9 + --> tests/ui/explicit_write.rs:22:9 | LL | write!(std::io::stdout(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `print!("test")` @@ -8,76 +8,76 @@ LL | write!(std::io::stdout(), "test").unwrap(); = help: to override `-D warnings` add `#[allow(clippy::explicit_write)]` error: use of `write!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:25:9 + --> tests/ui/explicit_write.rs:24:9 | LL | write!(std::io::stderr(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprint!("test")` error: use of `writeln!(stdout(), ...).unwrap()` - --> tests/ui/explicit_write.rs:27:9 + --> tests/ui/explicit_write.rs:26:9 | LL | writeln!(std::io::stdout(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `println!("test")` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:29:9 + --> tests/ui/explicit_write.rs:28:9 | LL | writeln!(std::io::stderr(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("test")` error: use of `stdout().write_fmt(...).unwrap()` - --> tests/ui/explicit_write.rs:31:9 + --> tests/ui/explicit_write.rs:30:9 | LL | std::io::stdout().write_fmt(format_args!("test")).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `print!("test")` error: use of `stderr().write_fmt(...).unwrap()` - --> tests/ui/explicit_write.rs:33:9 + --> tests/ui/explicit_write.rs:32:9 | LL | std::io::stderr().write_fmt(format_args!("test")).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprint!("test")` error: use of `writeln!(stdout(), ...).unwrap()` - --> tests/ui/explicit_write.rs:37:9 + --> tests/ui/explicit_write.rs:36:9 | LL | writeln!(std::io::stdout(), "test\ntest").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `println!("test\ntest")` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:39:9 + --> tests/ui/explicit_write.rs:38:9 | LL | writeln!(std::io::stderr(), "test\ntest").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("test\ntest")` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:43:9 + --> tests/ui/explicit_write.rs:42:9 | -LL | writeln!(std::io::stderr(), "with {}", value).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("with {}", value)` +LL | writeln!(std::io::stderr(), "with {value}").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("with {value}")` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:45:9 + --> tests/ui/explicit_write.rs:44:9 | LL | writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("with {} {}", 2, value)` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:47:9 + --> tests/ui/explicit_write.rs:46:9 | LL | writeln!(std::io::stderr(), "with {value}").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("with {value}")` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:49:9 + --> tests/ui/explicit_write.rs:48:9 | LL | writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("macro arg {}", one!())` error: use of `writeln!(stderr(), ...).unwrap()` - --> tests/ui/explicit_write.rs:52:9 + --> tests/ui/explicit_write.rs:51:9 | -LL | writeln!(std::io::stderr(), "{:w$}", value, w = width).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("{:w$}", value, w = width)` +LL | writeln!(std::io::stderr(), "{value:w$}", w = width).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `eprintln!("{value:w$}", w = width)` error: aborting due to 13 previous errors diff --git a/tests/ui/fallible_impl_from.rs b/tests/ui/fallible_impl_from.rs index 1c62c1e937b63..28bb1157f6e52 100644 --- a/tests/ui/fallible_impl_from.rs +++ b/tests/ui/fallible_impl_from.rs @@ -1,5 +1,4 @@ #![deny(clippy::fallible_impl_from)] -#![allow(clippy::uninlined_format_args)] // docs example struct Foo(i32); @@ -62,7 +61,7 @@ impl<'a> From<&'a mut as ProjStrTrait>::ProjString> for Invalid { fn from(s: &'a mut as ProjStrTrait>::ProjString) -> Invalid { if s.parse::().ok().unwrap() != 42 { - panic!("{:?}", s); + panic!("{s:?}"); } Invalid } diff --git a/tests/ui/fallible_impl_from.stderr b/tests/ui/fallible_impl_from.stderr index 402494b39f30b..25ecc8b0a39a2 100644 --- a/tests/ui/fallible_impl_from.stderr +++ b/tests/ui/fallible_impl_from.stderr @@ -1,5 +1,5 @@ error: consider implementing `TryFrom` instead - --> tests/ui/fallible_impl_from.rs:6:1 + --> tests/ui/fallible_impl_from.rs:5:1 | LL | / impl From for Foo { LL | | @@ -11,7 +11,7 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> tests/ui/fallible_impl_from.rs:10:13 + --> tests/ui/fallible_impl_from.rs:9:13 | LL | Foo(s.parse().unwrap()) | ^^^^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | #![deny(clippy::fallible_impl_from)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider implementing `TryFrom` instead - --> tests/ui/fallible_impl_from.rs:29:1 + --> tests/ui/fallible_impl_from.rs:28:1 | LL | / impl From for Invalid { LL | | @@ -34,13 +34,13 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> tests/ui/fallible_impl_from.rs:34:13 + --> tests/ui/fallible_impl_from.rs:33:13 | LL | panic!(); | ^^^^^^^^ error: consider implementing `TryFrom` instead - --> tests/ui/fallible_impl_from.rs:40:1 + --> tests/ui/fallible_impl_from.rs:39:1 | LL | / impl From> for Invalid { LL | | @@ -52,7 +52,7 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> tests/ui/fallible_impl_from.rs:44:17 + --> tests/ui/fallible_impl_from.rs:43:17 | LL | let s = s.unwrap(); | ^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | panic!("{:?}", s); | ^^^^^^^^^^^^^^^^^ error: consider implementing `TryFrom` instead - --> tests/ui/fallible_impl_from.rs:60:1 + --> tests/ui/fallible_impl_from.rs:59:1 | LL | / impl<'a> From<&'a mut as ProjStrTrait>::ProjString> for Invalid { LL | | @@ -77,12 +77,12 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> tests/ui/fallible_impl_from.rs:64:12 + --> tests/ui/fallible_impl_from.rs:63:12 | LL | if s.parse::().ok().unwrap() != 42 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | panic!("{:?}", s); - | ^^^^^^^^^^^^^^^^^ +LL | panic!("{s:?}"); + | ^^^^^^^^^^^^^^^ error: aborting due to 4 previous errors diff --git a/tests/ui/floating_point_log.fixed b/tests/ui/floating_point_log.fixed index 275c9b4a3ab9a..e831e30a71d81 100644 --- a/tests/ui/floating_point_log.fixed +++ b/tests/ui/floating_point_log.fixed @@ -14,10 +14,8 @@ fn check_log_base() { //~^ suboptimal_flops let _ = x.ln(); //~^ suboptimal_flops - let _ = x.log2(); - //~^ suboptimal_flops - let _ = x.ln(); - //~^ suboptimal_flops + let _ = x.log(TWO); + let _ = x.log(E); let _ = (x as f32).log2(); //~^ suboptimal_flops diff --git a/tests/ui/floating_point_log.rs b/tests/ui/floating_point_log.rs index a372ccbb9fb03..06cb1c8d96039 100644 --- a/tests/ui/floating_point_log.rs +++ b/tests/ui/floating_point_log.rs @@ -15,9 +15,7 @@ fn check_log_base() { let _ = x.log(std::f32::consts::E); //~^ suboptimal_flops let _ = x.log(TWO); - //~^ suboptimal_flops let _ = x.log(E); - //~^ suboptimal_flops let _ = (x as f32).log(2f32); //~^ suboptimal_flops diff --git a/tests/ui/floating_point_log.stderr b/tests/ui/floating_point_log.stderr index e93b3af851cbe..3e141de626d94 100644 --- a/tests/ui/floating_point_log.stderr +++ b/tests/ui/floating_point_log.stderr @@ -19,44 +19,32 @@ error: logarithm for bases 2, 10 and e can be computed more accurately LL | let _ = x.log(std::f32::consts::E); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()` -error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:17:13 - | -LL | let _ = x.log(TWO); - | ^^^^^^^^^^ help: consider using: `x.log2()` - error: logarithm for bases 2, 10 and e can be computed more accurately --> tests/ui/floating_point_log.rs:19:13 | -LL | let _ = x.log(E); - | ^^^^^^^^ help: consider using: `x.ln()` - -error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:21:13 - | LL | let _ = (x as f32).log(2f32); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).log2()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:25:13 + --> tests/ui/floating_point_log.rs:23:13 | LL | let _ = x.log(2f64); | ^^^^^^^^^^^ help: consider using: `x.log2()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:27:13 + --> tests/ui/floating_point_log.rs:25:13 | LL | let _ = x.log(10f64); | ^^^^^^^^^^^^ help: consider using: `x.log10()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> tests/ui/floating_point_log.rs:29:13 + --> tests/ui/floating_point_log.rs:27:13 | LL | let _ = x.log(std::f64::consts::E); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:35:13 + --> tests/ui/floating_point_log.rs:33:13 | LL | let _ = (1f32 + 2.).ln(); | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()` @@ -65,118 +53,118 @@ LL | let _ = (1f32 + 2.).ln(); = help: to override `-D warnings` add `#[allow(clippy::imprecise_flops)]` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:37:13 + --> tests/ui/floating_point_log.rs:35:13 | LL | let _ = (1f32 + 2.0).ln(); | ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:39:13 + --> tests/ui/floating_point_log.rs:37:13 | LL | let _ = (1.0 + x).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:41:13 + --> tests/ui/floating_point_log.rs:39:13 | LL | let _ = (1.0 + x / 2.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:43:13 + --> tests/ui/floating_point_log.rs:41:13 | LL | let _ = (1.0 + x.powi(3)).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:45:13 + --> tests/ui/floating_point_log.rs:43:13 | LL | let _ = (1.0 + x.powi(3) / 2.0).ln(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(3) / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:47:13 + --> tests/ui/floating_point_log.rs:45:13 | LL | let _ = (1.0 + (std::f32::consts::E - 1.0)).ln(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(std::f32::consts::E - 1.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:49:13 + --> tests/ui/floating_point_log.rs:47:13 | LL | let _ = (x + 1.0).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:51:13 + --> tests/ui/floating_point_log.rs:49:13 | LL | let _ = (x.powi(3) + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:53:13 + --> tests/ui/floating_point_log.rs:51:13 | LL | let _ = (x + 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:55:13 + --> tests/ui/floating_point_log.rs:53:13 | LL | let _ = (x / 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:64:13 + --> tests/ui/floating_point_log.rs:62:13 | LL | let _ = (1f64 + 2.).ln(); | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:66:13 + --> tests/ui/floating_point_log.rs:64:13 | LL | let _ = (1f64 + 2.0).ln(); | ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:68:13 + --> tests/ui/floating_point_log.rs:66:13 | LL | let _ = (1.0 + x).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:70:13 + --> tests/ui/floating_point_log.rs:68:13 | LL | let _ = (1.0 + x / 2.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:72:13 + --> tests/ui/floating_point_log.rs:70:13 | LL | let _ = (1.0 + x.powi(3)).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:74:13 + --> tests/ui/floating_point_log.rs:72:13 | LL | let _ = (x + 1.0).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:76:13 + --> tests/ui/floating_point_log.rs:74:13 | LL | let _ = (x.powi(3) + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:78:13 + --> tests/ui/floating_point_log.rs:76:13 | LL | let _ = (x + 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> tests/ui/floating_point_log.rs:80:13 + --> tests/ui/floating_point_log.rs:78:13 | LL | let _ = (x / 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` -error: aborting due to 29 previous errors +error: aborting due to 27 previous errors diff --git a/tests/ui/if_then_some_else_none.fixed b/tests/ui/if_then_some_else_none.fixed index 0fd130609aee2..7da9401a308f7 100644 --- a/tests/ui/if_then_some_else_none.fixed +++ b/tests/ui/if_then_some_else_none.fixed @@ -206,3 +206,15 @@ fn dont_lint_inside_macros() { } let _: Option = mac!(true, 42); } + +mod issue15770 { + fn maybe_error() -> Result { + Err("error!") + } + + pub fn trying(b: bool) -> Result<(), &'static str> { + let _x: Option = if b { Some(maybe_error()?) } else { None }; + // Process _x locally + Ok(()) + } +} diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs index 640828aa9bf6b..02962f83ce8aa 100644 --- a/tests/ui/if_then_some_else_none.rs +++ b/tests/ui/if_then_some_else_none.rs @@ -262,3 +262,15 @@ fn dont_lint_inside_macros() { } let _: Option = mac!(true, 42); } + +mod issue15770 { + fn maybe_error() -> Result { + Err("error!") + } + + pub fn trying(b: bool) -> Result<(), &'static str> { + let _x: Option = if b { Some(maybe_error()?) } else { None }; + // Process _x locally + Ok(()) + } +} diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.fixed b/tests/ui/index_refutable_slice/if_let_slice_binding.fixed index 050cdfcba966e..dc7e09bbdc7d2 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.fixed +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.fixed @@ -1,5 +1,5 @@ #![deny(clippy::index_refutable_slice)] -#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes, clippy::collapsible_if)] +#![allow(clippy::needless_lifetimes, clippy::collapsible_if)] enum SomeEnum { One(T), @@ -60,7 +60,7 @@ fn lintable_examples() { println!("{:?}", slice_1); } - println!("{:?}", slice); + println!("{slice:?}"); // This should not suggest using the `ref` keyword as the scrutinee is already // a reference @@ -70,7 +70,7 @@ fn lintable_examples() { println!("{:?}", slice_0); } - println!("{:?}", slice); + println!("{slice:?}"); } fn slice_index_above_limit() { @@ -113,7 +113,7 @@ fn check_slice_as_arg() { println!("This is interesting {}", slice[0]); } } - println!("{:?}", slice_wrapped); + println!("{slice_wrapped:?}"); } fn check_slice_in_struct() { @@ -152,7 +152,7 @@ fn check_slice_in_struct() { println!("This is super awesome! {}", slice_0); } } - println!("Complete wrap: {:?}", wrap); + println!("Complete wrap: {wrap:?}"); } /// This would be a nice additional feature to have in the future, but adding it @@ -164,14 +164,14 @@ fn mutable_slice_index() { if let Some(ref mut slice) = slice { slice[0] = String::from("Mr. Penguin"); } - println!("Use after modification: {:?}", slice); + println!("Use after modification: {slice:?}"); // Mut access on reference let mut slice: Option<[String; 1]> = Some([String::from("Cat")]); if let Some(slice) = &mut slice { slice[0] = String::from("Lord Meow Meow"); } - println!("Use after modification: {:?}", slice); + println!("Use after modification: {slice:?}"); } /// The lint will ignore bindings with sub patterns as it would be hard diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.rs b/tests/ui/index_refutable_slice/if_let_slice_binding.rs index 91429bfea2762..f39ace101b45c 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.rs +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.rs @@ -1,5 +1,5 @@ #![deny(clippy::index_refutable_slice)] -#![allow(clippy::uninlined_format_args, clippy::needless_lifetimes, clippy::collapsible_if)] +#![allow(clippy::needless_lifetimes, clippy::collapsible_if)] enum SomeEnum { One(T), @@ -60,7 +60,7 @@ fn lintable_examples() { println!("{:?}", slice[1]); } - println!("{:?}", slice); + println!("{slice:?}"); // This should not suggest using the `ref` keyword as the scrutinee is already // a reference @@ -70,7 +70,7 @@ fn lintable_examples() { println!("{:?}", slice[0]); } - println!("{:?}", slice); + println!("{slice:?}"); } fn slice_index_above_limit() { @@ -113,7 +113,7 @@ fn check_slice_as_arg() { println!("This is interesting {}", slice[0]); } } - println!("{:?}", slice_wrapped); + println!("{slice_wrapped:?}"); } fn check_slice_in_struct() { @@ -152,7 +152,7 @@ fn check_slice_in_struct() { println!("This is super awesome! {}", slice[0]); } } - println!("Complete wrap: {:?}", wrap); + println!("Complete wrap: {wrap:?}"); } /// This would be a nice additional feature to have in the future, but adding it @@ -164,14 +164,14 @@ fn mutable_slice_index() { if let Some(ref mut slice) = slice { slice[0] = String::from("Mr. Penguin"); } - println!("Use after modification: {:?}", slice); + println!("Use after modification: {slice:?}"); // Mut access on reference let mut slice: Option<[String; 1]> = Some([String::from("Cat")]); if let Some(slice) = &mut slice { slice[0] = String::from("Lord Meow Meow"); } - println!("Use after modification: {:?}", slice); + println!("Use after modification: {slice:?}"); } /// The lint will ignore bindings with sub patterns as it would be hard diff --git a/tests/ui/inefficient_to_string.fixed b/tests/ui/inefficient_to_string.fixed index a0d34e58a9255..29cf6de6ae5e8 100644 --- a/tests/ui/inefficient_to_string.fixed +++ b/tests/ui/inefficient_to_string.fixed @@ -2,6 +2,7 @@ use std::borrow::Cow; +#[clippy::msrv = "1.81"] fn main() { let rstr: &str = "hello"; let rrstr: &&str = &rstr; @@ -34,3 +35,10 @@ fn main() { let _: String = (**rrrcow).to_string(); //~^ inefficient_to_string } + +#[clippy::msrv = "1.82"] +fn sufficient_msrv() { + let rstr: &str = "hello"; + let rrstr: &&str = &rstr; + let _: String = rrstr.to_string(); +} diff --git a/tests/ui/inefficient_to_string.rs b/tests/ui/inefficient_to_string.rs index cbe90d4a125b0..724955c60f795 100644 --- a/tests/ui/inefficient_to_string.rs +++ b/tests/ui/inefficient_to_string.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; +#[clippy::msrv = "1.81"] fn main() { let rstr: &str = "hello"; let rrstr: &&str = &rstr; @@ -34,3 +35,10 @@ fn main() { let _: String = rrrcow.to_string(); //~^ inefficient_to_string } + +#[clippy::msrv = "1.82"] +fn sufficient_msrv() { + let rstr: &str = "hello"; + let rrstr: &&str = &rstr; + let _: String = rrstr.to_string(); +} diff --git a/tests/ui/inefficient_to_string.stderr b/tests/ui/inefficient_to_string.stderr index 8593c0addc5f8..ea3dd7e0ae2f8 100644 --- a/tests/ui/inefficient_to_string.stderr +++ b/tests/ui/inefficient_to_string.stderr @@ -1,5 +1,5 @@ error: calling `to_string` on `&&str` - --> tests/ui/inefficient_to_string.rs:10:21 + --> tests/ui/inefficient_to_string.rs:11:21 | LL | let _: String = rrstr.to_string(); | ^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrstr).to_string()` @@ -12,7 +12,7 @@ LL | #![deny(clippy::inefficient_to_string)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: calling `to_string` on `&&&str` - --> tests/ui/inefficient_to_string.rs:12:21 + --> tests/ui/inefficient_to_string.rs:13:21 | LL | let _: String = rrrstr.to_string(); | ^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrstr).to_string()` @@ -20,7 +20,7 @@ LL | let _: String = rrrstr.to_string(); = help: `&&str` implements `ToString` through a slower blanket impl, but `str` has a fast specialization of `ToString` error: calling `to_string` on `&&std::string::String` - --> tests/ui/inefficient_to_string.rs:21:21 + --> tests/ui/inefficient_to_string.rs:22:21 | LL | let _: String = rrstring.to_string(); | ^^^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrstring).to_string()` @@ -28,7 +28,7 @@ LL | let _: String = rrstring.to_string(); = help: `&std::string::String` implements `ToString` through a slower blanket impl, but `std::string::String` has a fast specialization of `ToString` error: calling `to_string` on `&&&std::string::String` - --> tests/ui/inefficient_to_string.rs:23:21 + --> tests/ui/inefficient_to_string.rs:24:21 | LL | let _: String = rrrstring.to_string(); | ^^^^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrstring).to_string()` @@ -36,7 +36,7 @@ LL | let _: String = rrrstring.to_string(); = help: `&&std::string::String` implements `ToString` through a slower blanket impl, but `std::string::String` has a fast specialization of `ToString` error: calling `to_string` on `&&std::borrow::Cow<'_, str>` - --> tests/ui/inefficient_to_string.rs:32:21 + --> tests/ui/inefficient_to_string.rs:33:21 | LL | let _: String = rrcow.to_string(); | ^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrcow).to_string()` @@ -44,7 +44,7 @@ LL | let _: String = rrcow.to_string(); = help: `&std::borrow::Cow<'_, str>` implements `ToString` through a slower blanket impl, but `std::borrow::Cow<'_, str>` has a fast specialization of `ToString` error: calling `to_string` on `&&&std::borrow::Cow<'_, str>` - --> tests/ui/inefficient_to_string.rs:34:21 + --> tests/ui/inefficient_to_string.rs:35:21 | LL | let _: String = rrrcow.to_string(); | ^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrcow).to_string()` diff --git a/tests/ui/infinite_iter.rs b/tests/ui/infinite_iter.rs index 701a86534ba00..4e1668ed04fb7 100644 --- a/tests/ui/infinite_iter.rs +++ b/tests/ui/infinite_iter.rs @@ -1,4 +1,4 @@ -#![allow(clippy::uninlined_format_args, clippy::double_ended_iterator_last)] +#![allow(clippy::double_ended_iterator_last)] use std::iter::repeat; fn square_is_lower_64(x: &u32) -> bool { @@ -30,7 +30,7 @@ fn infinite_iters() { .rev() .cycle() .map(|x| x + 1_u32) - .for_each(|x| println!("{}", x)); + .for_each(|x| println!("{x}")); // infinite iter (0..3_u32).flat_map(|x| x..).sum::(); // infinite iter diff --git a/tests/ui/infinite_iter.stderr b/tests/ui/infinite_iter.stderr index b9e7c008f93e0..3db97313b621d 100644 --- a/tests/ui/infinite_iter.stderr +++ b/tests/ui/infinite_iter.stderr @@ -30,8 +30,8 @@ LL | | LL | | .rev() LL | | .cycle() LL | | .map(|x| x + 1_u32) -LL | | .for_each(|x| println!("{}", x)); - | |________________________________________^ +LL | | .for_each(|x| println!("{x}")); + | |______________________________________^ error: infinite iteration detected --> tests/ui/infinite_iter.rs:37:5 diff --git a/tests/ui/ip_constant.fixed b/tests/ui/ip_constant.fixed index c947968213948..afdf581bacf7a 100644 --- a/tests/ui/ip_constant.fixed +++ b/tests/ui/ip_constant.fixed @@ -72,33 +72,44 @@ const CONST_U16_1: u16 = 1; fn const_test1() { use std::net::Ipv4Addr; - let _ = Ipv4Addr::LOCALHOST; - //~^ ip_constant - let _ = Ipv4Addr::BROADCAST; - //~^ ip_constant - let _ = Ipv4Addr::UNSPECIFIED; - //~^ ip_constant + let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1); + let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255); + let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0); use std::net::Ipv6Addr; - let _ = Ipv6Addr::LOCALHOST; - - let _ = Ipv6Addr::UNSPECIFIED; + let _ = Ipv6Addr::new( + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_1, + ); + + let _ = Ipv6Addr::new( + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + CONST_U16_0, + ); } fn const_test2() { use std::net::Ipv4Addr; let _ = Ipv4Addr::LOCALHOST; //~^ ip_constant - let _ = Ipv4Addr::BROADCAST; - //~^ ip_constant - let _ = Ipv4Addr::UNSPECIFIED; - //~^ ip_constant + let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255); + let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 }); use std::net::Ipv6Addr; - let _ = Ipv6Addr::LOCALHOST; - //~^ ip_constant - let _ = Ipv6Addr::LOCALHOST; - //~^ ip_constant + let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1); + let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1); } macro_rules! ipv4_new { diff --git a/tests/ui/ip_constant.rs b/tests/ui/ip_constant.rs index 69a5c3b4e9230..04fc2f0f6fdae 100644 --- a/tests/ui/ip_constant.rs +++ b/tests/ui/ip_constant.rs @@ -73,15 +73,11 @@ const CONST_U16_1: u16 = 1; fn const_test1() { use std::net::Ipv4Addr; let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1); - //~^ ip_constant let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255); - //~^ ip_constant let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0); - //~^ ip_constant use std::net::Ipv6Addr; let _ = Ipv6Addr::new( - //~^ ip_constant CONST_U16_0, CONST_U16_0, CONST_U16_0, @@ -93,7 +89,6 @@ fn const_test1() { ); let _ = Ipv6Addr::new( - //~^ ip_constant CONST_U16_0, CONST_U16_0, CONST_U16_0, @@ -110,15 +105,11 @@ fn const_test2() { let _ = Ipv4Addr::new(126 + 1, 0, 0, 1); //~^ ip_constant let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255); - //~^ ip_constant let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 }); - //~^ ip_constant use std::net::Ipv6Addr; let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1); - //~^ ip_constant let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1); - //~^ ip_constant } macro_rules! ipv4_new { diff --git a/tests/ui/ip_constant.stderr b/tests/ui/ip_constant.stderr index 07d912b18a57b..44e3d6448dbd8 100644 --- a/tests/ui/ip_constant.stderr +++ b/tests/ui/ip_constant.stderr @@ -241,101 +241,7 @@ LL + let _ = std::net::Ipv6Addr::UNSPECIFIED; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:75:13 - | -LL | let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1); -LL + let _ = Ipv4Addr::LOCALHOST; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:77:13 - | -LL | let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255); -LL + let _ = Ipv4Addr::BROADCAST; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:79:13 - | -LL | let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0); -LL + let _ = Ipv4Addr::UNSPECIFIED; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:83:13 - | -LL | let _ = Ipv6Addr::new( - | _____________^ -LL | | -LL | | CONST_U16_0, -LL | | CONST_U16_0, -... | -LL | | CONST_U16_1, -LL | | ); - | |_____^ - | -help: use - | -LL - let _ = Ipv6Addr::new( -LL - -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_1, -LL - ); -LL + let _ = Ipv6Addr::LOCALHOST; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:95:13 - | -LL | let _ = Ipv6Addr::new( - | _____________^ -LL | | -LL | | CONST_U16_0, -LL | | CONST_U16_0, -... | -LL | | CONST_U16_0, -LL | | ); - | |_____^ - | -help: use - | -LL - let _ = Ipv6Addr::new( -LL - -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - CONST_U16_0, -LL - ); -LL + let _ = Ipv6Addr::UNSPECIFIED; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:110:13 + --> tests/ui/ip_constant.rs:105:13 | LL | let _ = Ipv4Addr::new(126 + 1, 0, 0, 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -346,53 +252,5 @@ LL - let _ = Ipv4Addr::new(126 + 1, 0, 0, 1); LL + let _ = Ipv4Addr::LOCALHOST; | -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:112:13 - | -LL | let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255); -LL + let _ = Ipv4Addr::BROADCAST; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:114:13 - | -LL | let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 }); -LL + let _ = Ipv4Addr::UNSPECIFIED; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:118:13 - | -LL | let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1); -LL + let _ = Ipv6Addr::LOCALHOST; - | - -error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:120:13 - | -LL | let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use - | -LL - let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1); -LL + let _ = Ipv6Addr::LOCALHOST; - | - -error: aborting due to 30 previous errors +error: aborting due to 21 previous errors diff --git a/tests/ui/issue_2356.fixed b/tests/ui/issue_2356.fixed index 46ba653eba2cf..3e066df77bfb4 100644 --- a/tests/ui/issue_2356.fixed +++ b/tests/ui/issue_2356.fixed @@ -1,6 +1,5 @@ #![deny(clippy::while_let_on_iterator)] #![allow(unused_mut)] -#![allow(clippy::uninlined_format_args)] use std::iter::Iterator; @@ -16,7 +15,7 @@ impl Foo { fn foo2>(mut it: I) { for e in it { //~^ while_let_on_iterator - println!("{:?}", e); + println!("{e:?}"); } } } diff --git a/tests/ui/issue_2356.rs b/tests/ui/issue_2356.rs index defe2584a93e1..98600d17c6df7 100644 --- a/tests/ui/issue_2356.rs +++ b/tests/ui/issue_2356.rs @@ -1,6 +1,5 @@ #![deny(clippy::while_let_on_iterator)] #![allow(unused_mut)] -#![allow(clippy::uninlined_format_args)] use std::iter::Iterator; @@ -16,7 +15,7 @@ impl Foo { fn foo2>(mut it: I) { while let Some(e) = it.next() { //~^ while_let_on_iterator - println!("{:?}", e); + println!("{e:?}"); } } } diff --git a/tests/ui/issue_2356.stderr b/tests/ui/issue_2356.stderr index eae2ce97fc6b1..ddee91fcfcd52 100644 --- a/tests/ui/issue_2356.stderr +++ b/tests/ui/issue_2356.stderr @@ -1,5 +1,5 @@ error: this loop could be written as a `for` loop - --> tests/ui/issue_2356.rs:17:9 + --> tests/ui/issue_2356.rs:16:9 | LL | while let Some(e) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for e in it` diff --git a/tests/ui/issue_4266.rs b/tests/ui/issue_4266.rs index 664f0b84a2077..b2a01124995c5 100644 --- a/tests/ui/issue_4266.rs +++ b/tests/ui/issue_4266.rs @@ -1,5 +1,4 @@ #![allow(dead_code)] -#![allow(clippy::uninlined_format_args)] async fn sink1<'a>(_: &'a str) {} // lint //~^ needless_lifetimes @@ -39,7 +38,7 @@ impl Foo { // rust-lang/rust#61115 // ok async fn print(s: &str) { - println!("{}", s); + println!("{s}"); } fn main() {} diff --git a/tests/ui/issue_4266.stderr b/tests/ui/issue_4266.stderr index 0e181025430f3..b80a738a50bee 100644 --- a/tests/ui/issue_4266.stderr +++ b/tests/ui/issue_4266.stderr @@ -1,5 +1,5 @@ error: the following explicit lifetimes could be elided: 'a - --> tests/ui/issue_4266.rs:4:16 + --> tests/ui/issue_4266.rs:3:16 | LL | async fn sink1<'a>(_: &'a str) {} // lint | ^^ ^^ @@ -8,13 +8,13 @@ LL | async fn sink1<'a>(_: &'a str) {} // lint = help: to override `-D warnings` add `#[allow(clippy::needless_lifetimes)]` error: the following explicit lifetimes could be elided: 'a - --> tests/ui/issue_4266.rs:10:21 + --> tests/ui/issue_4266.rs:9:21 | LL | async fn one_to_one<'a>(s: &'a str) -> &'a str { | ^^ ^^ ^^ error: methods called `new` usually take no `self` - --> tests/ui/issue_4266.rs:32:22 + --> tests/ui/issue_4266.rs:31:22 | LL | pub async fn new(&mut self) -> Self { | ^^^^^^^^^ diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed index 381d4cac4622b..6d984a495d2bd 100644 --- a/tests/ui/let_unit.fixed +++ b/tests/ui/let_unit.fixed @@ -1,5 +1,10 @@ #![warn(clippy::let_unit_value)] -#![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)] +#![allow( + clippy::no_effect, + clippy::needless_late_init, + path_statements, + clippy::match_single_binding +)] macro_rules! let_and_return { ($n:expr) => {{ @@ -15,12 +20,12 @@ fn main() { if true { // do not lint this, since () is explicit let _a = (); - let () = dummy(); + let () = returns_unit(); let () = (); - () = dummy(); + () = returns_unit(); () = (); let _a: () = (); - let _a: () = dummy(); + let _a: () = returns_unit(); } consume_units_with_for_loop(); // should be fine as well @@ -30,7 +35,7 @@ fn main() { let_and_return!(()) // should be fine } -fn dummy() {} +fn returns_unit() {} // Related to issue #1964 fn consume_units_with_for_loop() { @@ -181,8 +186,6 @@ async fn issue10433() { pub async fn issue11502(a: ()) {} pub fn issue12594() { - fn returns_unit() {} - fn returns_result(res: T) -> Result { Ok(res) } @@ -199,13 +202,40 @@ pub fn issue12594() { } } +fn takes_unit(x: ()) {} + fn issue15061() { - fn return_unit() {} - fn do_something(x: ()) {} + let res = (); + returns_unit(); + //~^ let_unit_value + takes_unit(()); + println!("{res:?}"); +} + +fn issue15771() { + match "Example String" { + _ => returns_unit(), + //~^ let_unit_value + } + if true {} + //~^ let_unit_value +} + +fn issue_15784() { let res = (); - return_unit(); + eprintln!("I return unit"); //~^ let_unit_value - do_something(()); + takes_unit(()); println!("{res:?}"); } + +fn issue15789() { + struct Foo { + value: (), + } + println!(); + //~^ let_unit_value + + Foo { value: () }; +} diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs index cdfc74991c40d..a0e32f0b67a02 100644 --- a/tests/ui/let_unit.rs +++ b/tests/ui/let_unit.rs @@ -1,5 +1,10 @@ #![warn(clippy::let_unit_value)] -#![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)] +#![allow( + clippy::no_effect, + clippy::needless_late_init, + path_statements, + clippy::match_single_binding +)] macro_rules! let_and_return { ($n:expr) => {{ @@ -15,12 +20,12 @@ fn main() { if true { // do not lint this, since () is explicit let _a = (); - let () = dummy(); + let () = returns_unit(); let () = (); - () = dummy(); + () = returns_unit(); () = (); let _a: () = (); - let _a: () = dummy(); + let _a: () = returns_unit(); } consume_units_with_for_loop(); // should be fine as well @@ -30,7 +35,7 @@ fn main() { let_and_return!(()) // should be fine } -fn dummy() {} +fn returns_unit() {} // Related to issue #1964 fn consume_units_with_for_loop() { @@ -181,8 +186,6 @@ async fn issue10433() { pub async fn issue11502(a: ()) {} pub fn issue12594() { - fn returns_unit() {} - fn returns_result(res: T) -> Result { Ok(res) } @@ -199,12 +202,38 @@ pub fn issue12594() { } } +fn takes_unit(x: ()) {} + fn issue15061() { - fn return_unit() {} - fn do_something(x: ()) {} + let res = returns_unit(); + //~^ let_unit_value + takes_unit(res); + println!("{res:?}"); +} + +fn issue15771() { + match "Example String" { + _ => _ = returns_unit(), + //~^ let_unit_value + } - let res = return_unit(); + _ = if true {} //~^ let_unit_value - do_something(res); +} + +fn issue_15784() { + let res = eprintln!("I return unit"); + //~^ let_unit_value + takes_unit(res); println!("{res:?}"); } + +fn issue15789() { + struct Foo { + value: (), + } + let value = println!(); + //~^ let_unit_value + + Foo { value }; +} diff --git a/tests/ui/let_unit.stderr b/tests/ui/let_unit.stderr index 637c9ff686bdb..6e7b958df4d9a 100644 --- a/tests/ui/let_unit.stderr +++ b/tests/ui/let_unit.stderr @@ -1,14 +1,19 @@ error: this let-binding has unit value - --> tests/ui/let_unit.rs:11:5 + --> tests/ui/let_unit.rs:16:5 | LL | let _x = println!("x"); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `println!("x");` + | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::let-unit-value` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::let_unit_value)]` +help: omit the `let` binding + | +LL - let _x = println!("x"); +LL + println!("x"); + | error: this let-binding has unit value - --> tests/ui/let_unit.rs:60:5 + --> tests/ui/let_unit.rs:65:5 | LL | / let _ = v LL | | @@ -21,18 +26,12 @@ LL | | .unwrap(); | help: omit the `let` binding | -LL ~ v -LL + -LL + .into_iter() -LL + .map(|i| i * 2) -LL + .filter(|i| i.is_multiple_of(2)) -LL + .map(|_| ()) -LL + .next() -LL + .unwrap(); +LL - let _ = v +LL + v | error: this let-binding has unit value - --> tests/ui/let_unit.rs:110:5 + --> tests/ui/let_unit.rs:115:5 | LL | / let x = match Some(0) { LL | | @@ -45,17 +44,12 @@ LL | | }; | help: omit the `let` binding | -LL ~ match Some(0) { -LL + -LL + None => f2(1), -LL + Some(0) => f(), -LL + Some(1) => f2(3), -LL + Some(_) => (), -LL + }; +LL - let x = match Some(0) { +LL + match Some(0) { | error: this let-binding has unit value - --> tests/ui/let_unit.rs:192:9 + --> tests/ui/let_unit.rs:195:9 | LL | let res = returns_unit(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -69,18 +63,70 @@ LL ~ returns_result(()).unwrap(); | error: this let-binding has unit value - --> tests/ui/let_unit.rs:206:5 + --> tests/ui/let_unit.rs:208:5 | -LL | let res = return_unit(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let res = returns_unit(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | help: replace variable usages with `()` | LL ~ let res = (); -LL ~ return_unit(); +LL ~ returns_unit(); +LL | +LL ~ takes_unit(()); + | + +error: this let-binding has unit value + --> tests/ui/let_unit.rs:216:14 + | +LL | _ => _ = returns_unit(), + | ^^^^^^^^^^^^^^^^^^ + | +help: omit the `let` binding + | +LL - _ => _ = returns_unit(), +LL + _ => returns_unit(), + | + +error: this let-binding has unit value + --> tests/ui/let_unit.rs:220:5 + | +LL | _ = if true {} + | ^^^^^^^^^^^^^^ + | +help: omit the `let` binding + | +LL - _ = if true {} +LL + if true {} + | + +error: this let-binding has unit value + --> tests/ui/let_unit.rs:225:5 + | +LL | let res = eprintln!("I return unit"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace variable usages with `()` + | +LL ~ let res = (); +LL ~ eprintln!("I return unit"); +LL | +LL ~ takes_unit(()); + | + +error: this let-binding has unit value + --> tests/ui/let_unit.rs:235:5 + | +LL | let value = println!(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: omit the `let` binding and replace variable usages with `()` + | +LL ~ println!(); +LL | LL | -LL ~ do_something(()); +LL ~ Foo { value: () }; | -error: aborting due to 5 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/manual_float_methods.rs b/tests/ui/manual_float_methods.rs index 62cdc1c141d0c..4b496a4932833 100644 --- a/tests/ui/manual_float_methods.rs +++ b/tests/ui/manual_float_methods.rs @@ -8,9 +8,6 @@ #[macro_use] extern crate proc_macros; -const INFINITE: f32 = f32::INFINITY; -const NEG_INFINITE: f32 = f32::NEG_INFINITY; - fn fn_test() -> f64 { f64::NEG_INFINITY } @@ -25,10 +22,6 @@ fn main() { //~^ manual_is_infinite if x != f32::INFINITY && x != f32::NEG_INFINITY {} //~^ manual_is_finite - if x == INFINITE || x == NEG_INFINITE {} - //~^ manual_is_infinite - if x != INFINITE && x != NEG_INFINITE {} - //~^ manual_is_finite let x = 1.0f64; if x == f64::INFINITY || x == f64::NEG_INFINITY {} //~^ manual_is_infinite @@ -64,4 +57,12 @@ fn main() { if x == f32::INFINITY || x == f32::NEG_INFINITY {} if x != f32::INFINITY && x != f32::NEG_INFINITY {} } + + { + let x = 1.0f32; + const X: f32 = f32::INFINITY; + const Y: f32 = f32::NEG_INFINITY; + if x == X || x == Y {} + if x != X && x != Y {} + } } diff --git a/tests/ui/manual_float_methods.stderr b/tests/ui/manual_float_methods.stderr index 352c879c87d73..0a27e0eac48b3 100644 --- a/tests/ui/manual_float_methods.stderr +++ b/tests/ui/manual_float_methods.stderr @@ -1,5 +1,5 @@ error: manually checking if a float is infinite - --> tests/ui/manual_float_methods.rs:24:8 + --> tests/ui/manual_float_methods.rs:21:8 | LL | if x == f32::INFINITY || x == f32::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` @@ -8,7 +8,7 @@ LL | if x == f32::INFINITY || x == f32::NEG_INFINITY {} = help: to override `-D warnings` add `#[allow(clippy::manual_is_infinite)]` error: manually checking if a float is finite - --> tests/ui/manual_float_methods.rs:26:8 + --> tests/ui/manual_float_methods.rs:23:8 | LL | if x != f32::INFINITY && x != f32::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,41 +32,13 @@ LL + if !x.is_infinite() {} | error: manually checking if a float is infinite - --> tests/ui/manual_float_methods.rs:28:8 - | -LL | if x == INFINITE || x == NEG_INFINITE {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` - -error: manually checking if a float is finite - --> tests/ui/manual_float_methods.rs:30:8 - | -LL | if x != INFINITE && x != NEG_INFINITE {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use the dedicated method instead - | -LL - if x != INFINITE && x != NEG_INFINITE {} -LL + if x.is_finite() {} - | -help: this will alter how it handles NaN; if that is a problem, use instead - | -LL - if x != INFINITE && x != NEG_INFINITE {} -LL + if x.is_finite() || x.is_nan() {} - | -help: or, for conciseness - | -LL - if x != INFINITE && x != NEG_INFINITE {} -LL + if !x.is_infinite() {} - | - -error: manually checking if a float is infinite - --> tests/ui/manual_float_methods.rs:33:8 + --> tests/ui/manual_float_methods.rs:26:8 | LL | if x == f64::INFINITY || x == f64::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` error: manually checking if a float is finite - --> tests/ui/manual_float_methods.rs:35:8 + --> tests/ui/manual_float_methods.rs:28:8 | LL | if x != f64::INFINITY && x != f64::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,10 +60,10 @@ LL + if !x.is_infinite() {} | error: manually checking if a float is infinite - --> tests/ui/manual_float_methods.rs:50:12 + --> tests/ui/manual_float_methods.rs:43:12 | LL | if x == f64::INFINITY || x == f64::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` -error: aborting due to 7 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/manual_instant_elapsed.fixed b/tests/ui/manual_instant_elapsed.fixed index 187802bb76c9e..a04c601e08c10 100644 --- a/tests/ui/manual_instant_elapsed.fixed +++ b/tests/ui/manual_instant_elapsed.fixed @@ -1,6 +1,6 @@ #![warn(clippy::manual_instant_elapsed)] #![allow(clippy::unnecessary_operation)] -#![allow(clippy::unchecked_duration_subtraction)] +#![allow(clippy::unchecked_time_subtraction)] #![allow(unused_variables)] #![allow(unused_must_use)] diff --git a/tests/ui/manual_instant_elapsed.rs b/tests/ui/manual_instant_elapsed.rs index 61e14e5a3d9d3..7c67f6acf85de 100644 --- a/tests/ui/manual_instant_elapsed.rs +++ b/tests/ui/manual_instant_elapsed.rs @@ -1,6 +1,6 @@ #![warn(clippy::manual_instant_elapsed)] #![allow(clippy::unnecessary_operation)] -#![allow(clippy::unchecked_duration_subtraction)] +#![allow(clippy::unchecked_time_subtraction)] #![allow(unused_variables)] #![allow(unused_must_use)] diff --git a/tests/ui/manual_strip.stderr b/tests/ui/manual_strip.stderr index a323ef700e767..d147cdae1f3b1 100644 --- a/tests/ui/manual_strip.stderr +++ b/tests/ui/manual_strip.stderr @@ -97,9 +97,6 @@ LL | if s.starts_with(PREFIX) { help: try using the `strip_prefix` method | LL ~ if let Some() = s.strip_prefix(PREFIX) { -LL ~ str::to_string(); -LL | -LL | LL ~ str::to_string(); | diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index e29fb87dbc300..7e899a4766661 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -4,7 +4,6 @@ clippy::let_unit_value, clippy::no_effect, clippy::toplevel_ref_arg, - clippy::uninlined_format_args, clippy::useless_vec )] @@ -32,11 +31,11 @@ fn main() { // Lint let (x, y, z) = (a, b, c); { - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); } // Lint let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); // Ok foo!(a); // Ok @@ -47,7 +46,7 @@ fn main() { // Ok let d = Some(5); match d { - Some(d) => println!("{}", d), + Some(d) => println!("{d}"), _ => println!("None"), } // Lint @@ -55,7 +54,7 @@ fn main() { // Lint { let x = 29; - println!("x has a value of {}", x); + println!("x has a value of {x}"); } // Lint { @@ -67,18 +66,18 @@ fn main() { // Lint let p = Point { x: 0, y: 7 }; let Point { x, y } = p; - println!("Coords: ({}, {})", x, y); + println!("Coords: ({x}, {y})"); // Lint let Point { x: x1, y: y1 } = p; - println!("Coords: ({}, {})", x1, y1); + println!("Coords: ({x1}, {y1})"); // Lint let x = 5; let ref r = x; - println!("Got a reference to {}", r); + println!("Got a reference to {r}"); // Lint let mut x = 5; let ref mut mr = x; - println!("Got a mutable reference to {}", mr); + println!("Got a mutable reference to {mr}"); // Lint let Point { x, y } = coords(); let product = x * y; @@ -122,7 +121,7 @@ fn issue_8723() { let (pre, suf) = val.split_at(idx); val = { - println!("{}", pre); + println!("{pre}"); suf }; @@ -210,20 +209,20 @@ mod issue15018 { let x = 1; { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); } println!("x = {x}"); } fn not_used_later(a: i32, b: i32, c: i32) { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z) + println!("{x} {y} {z}") } #[allow(irrefutable_let_patterns)] fn not_used_later_but_shadowed(a: i32, b: i32, c: i32) { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); let x = 1; println!("x = {x}"); } @@ -231,27 +230,27 @@ mod issue15018 { #[allow(irrefutable_let_patterns)] fn not_used_later_but_shadowed_nested(a: i32, b: i32, c: i32) { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {x} {y}"); if let (x, y, z) = (a, b, c) { - println!("{} {} {}", x, y, z) + println!("{x} {y} {z}") } { let x: i32 = 1; { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); } if let (x, y, z) = (a, x, c) { - println!("{} {} {}", x, y, z) + println!("{x} {y} {z}") } } { let (x, y, z) = (a, b, c); - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); let fn_ = |y| { - println!("{} {} {}", a, b, y); + println!("{a} {b} {y}"); }; fn_(c); } diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index ede1ab32beb5f..37a96f2287c85 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -4,7 +4,6 @@ clippy::let_unit_value, clippy::no_effect, clippy::toplevel_ref_arg, - clippy::uninlined_format_args, clippy::useless_vec )] @@ -33,13 +32,13 @@ fn main() { match (a, b, c) { //~^ match_single_binding (x, y, z) => { - println!("{} {} {}", x, y, z); + println!("{x} {y} {z}"); }, } // Lint match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } // Ok foo!(a); @@ -51,7 +50,7 @@ fn main() { // Ok let d = Some(5); match d { - Some(d) => println!("{}", d), + Some(d) => println!("{d}"), _ => println!("None"), } // Lint @@ -64,7 +63,7 @@ fn main() { //~^ match_single_binding _ => { let x = 29; - println!("x has a value of {}", x); + println!("x has a value of {x}"); }, } // Lint @@ -81,24 +80,24 @@ fn main() { let p = Point { x: 0, y: 7 }; match p { //~^ match_single_binding - Point { x, y } => println!("Coords: ({}, {})", x, y), + Point { x, y } => println!("Coords: ({x}, {y})"), } // Lint match p { //~^ match_single_binding - Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1), + Point { x: x1, y: y1 } => println!("Coords: ({x1}, {y1})"), } // Lint let x = 5; match x { //~^ match_single_binding - ref r => println!("Got a reference to {}", r), + ref r => println!("Got a reference to {r}"), } // Lint let mut x = 5; match x { //~^ match_single_binding - ref mut mr => println!("Got a mutable reference to {}", mr), + ref mut mr => println!("Got a mutable reference to {mr}"), } // Lint let product = match coords() { @@ -150,7 +149,7 @@ fn issue_8723() { val = match val.split_at(idx) { //~^ match_single_binding (pre, suf) => { - println!("{}", pre); + println!("{pre}"); suf }, }; @@ -273,7 +272,7 @@ mod issue15018 { let x = 1; match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } println!("x = {x}"); } @@ -281,7 +280,7 @@ mod issue15018 { fn not_used_later(a: i32, b: i32, c: i32) { match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } } @@ -289,7 +288,7 @@ mod issue15018 { fn not_used_later_but_shadowed(a: i32, b: i32, c: i32) { match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } let x = 1; println!("x = {x}"); @@ -299,30 +298,30 @@ mod issue15018 { fn not_used_later_but_shadowed_nested(a: i32, b: i32, c: i32) { match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {x} {y}"), } if let (x, y, z) = (a, b, c) { - println!("{} {} {}", x, y, z) + println!("{x} {y} {z}") } { let x: i32 = 1; match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } if let (x, y, z) = (a, x, c) { - println!("{} {} {}", x, y, z) + println!("{x} {y} {z}") } } { match (a, b, c) { //~^ match_single_binding - (x, y, z) => println!("{} {} {}", x, y, z), + (x, y, z) => println!("{x} {y} {z}"), } let fn_ = |y| { - println!("{} {} {}", a, b, y); + println!("{a} {b} {y}"); }; fn_(c); } diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index eea71777890e5..82fc43aaa5eae 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -1,10 +1,10 @@ error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:33:5 + --> tests/ui/match_single_binding.rs:32:5 | LL | / match (a, b, c) { LL | | LL | | (x, y, z) => { -LL | | println!("{} {} {}", x, y, z); +LL | | println!("{x} {y} {z}"); LL | | }, LL | | } | |_____^ @@ -15,27 +15,27 @@ help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); LL + { -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); LL + } | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:40:5 + --> tests/ui/match_single_binding.rs:39:5 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_____^ | help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:58:5 + --> tests/ui/match_single_binding.rs:57:5 | LL | / match a { LL | | @@ -44,13 +44,13 @@ LL | | } | |_____^ help: consider using the match body instead: `println!("whatever");` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:63:5 + --> tests/ui/match_single_binding.rs:62:5 | LL | / match a { LL | | LL | | _ => { LL | | let x = 29; -LL | | println!("x has a value of {}", x); +LL | | println!("x has a value of {x}"); LL | | }, LL | | } | |_____^ @@ -59,12 +59,12 @@ help: consider using the match body instead | LL ~ { LL + let x = 29; -LL + println!("x has a value of {}", x); +LL + println!("x has a value of {x}"); LL + } | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:71:5 + --> tests/ui/match_single_binding.rs:70:5 | LL | / match a { LL | | @@ -86,67 +86,67 @@ LL + } | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:82:5 + --> tests/ui/match_single_binding.rs:81:5 | LL | / match p { LL | | -LL | | Point { x, y } => println!("Coords: ({}, {})", x, y), +LL | | Point { x, y } => println!("Coords: ({x}, {y})"), LL | | } | |_____^ | help: consider using a `let` statement | LL ~ let Point { x, y } = p; -LL + println!("Coords: ({}, {})", x, y); +LL + println!("Coords: ({x}, {y})"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:87:5 + --> tests/ui/match_single_binding.rs:86:5 | LL | / match p { LL | | -LL | | Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1), +LL | | Point { x: x1, y: y1 } => println!("Coords: ({x1}, {y1})"), LL | | } | |_____^ | help: consider using a `let` statement | LL ~ let Point { x: x1, y: y1 } = p; -LL + println!("Coords: ({}, {})", x1, y1); +LL + println!("Coords: ({x1}, {y1})"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:93:5 + --> tests/ui/match_single_binding.rs:92:5 | LL | / match x { LL | | -LL | | ref r => println!("Got a reference to {}", r), +LL | | ref r => println!("Got a reference to {r}"), LL | | } | |_____^ | help: consider using a `let` statement | LL ~ let ref r = x; -LL + println!("Got a reference to {}", r); +LL + println!("Got a reference to {r}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:99:5 + --> tests/ui/match_single_binding.rs:98:5 | LL | / match x { LL | | -LL | | ref mut mr => println!("Got a mutable reference to {}", mr), +LL | | ref mut mr => println!("Got a mutable reference to {mr}"), LL | | } | |_____^ | help: consider using a `let` statement | LL ~ let ref mut mr = x; -LL + println!("Got a mutable reference to {}", mr); +LL + println!("Got a mutable reference to {mr}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:104:5 + --> tests/ui/match_single_binding.rs:103:5 | LL | / let product = match coords() { LL | | @@ -161,7 +161,7 @@ LL + let product = x * y; | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:113:18 + --> tests/ui/match_single_binding.rs:112:18 | LL | .map(|i| match i.unwrap() { | __________________^ @@ -179,7 +179,7 @@ LL ~ }) | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:140:5 + --> tests/ui/match_single_binding.rs:139:5 | LL | / match x { LL | | @@ -189,12 +189,12 @@ LL | | } | |_____^ help: consider using the match body instead: `println!("Not an array index start")` error: this assignment could be simplified - --> tests/ui/match_single_binding.rs:150:5 + --> tests/ui/match_single_binding.rs:149:5 | LL | / val = match val.split_at(idx) { LL | | LL | | (pre, suf) => { -LL | | println!("{}", pre); +LL | | println!("{pre}"); LL | | suf LL | | }, LL | | }; @@ -204,13 +204,13 @@ help: consider removing the `match` expression | LL ~ let (pre, suf) = val.split_at(idx); LL + val = { -LL + println!("{}", pre); +LL + println!("{pre}"); LL + suf LL ~ }; | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding.rs:164:16 + --> tests/ui/match_single_binding.rs:163:16 | LL | let _ = || match side_effects() { | ________________^ @@ -228,7 +228,7 @@ LL ~ }; | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:171:5 + --> tests/ui/match_single_binding.rs:170:5 | LL | / match r { LL | | @@ -253,7 +253,7 @@ LL ~ }; | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:185:5 + --> tests/ui/match_single_binding.rs:184:5 | LL | / match 1 { LL | | @@ -262,7 +262,7 @@ LL | | } | |_____^ help: consider using the match body instead: `();` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:190:13 + --> tests/ui/match_single_binding.rs:189:13 | LL | let a = match 1 { | _____________^ @@ -272,7 +272,7 @@ LL | | }; | |_____^ help: consider using the match body instead: `()` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:195:5 + --> tests/ui/match_single_binding.rs:194:5 | LL | / match 1 { LL | | @@ -281,7 +281,7 @@ LL | | } | |_____^ help: consider using the match body instead: `side_effects();` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:200:13 + --> tests/ui/match_single_binding.rs:199:13 | LL | let b = match 1 { | _____________^ @@ -291,7 +291,7 @@ LL | | }; | |_____^ help: consider using the match body instead: `side_effects()` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:205:5 + --> tests/ui/match_single_binding.rs:204:5 | LL | / match 1 { LL | | @@ -300,7 +300,7 @@ LL | | } | |_____^ help: consider using the match body instead: `println!("1");` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:210:13 + --> tests/ui/match_single_binding.rs:209:13 | LL | let c = match 1 { | _____________^ @@ -310,7 +310,7 @@ LL | | }; | |_____^ help: consider using the match body instead: `println!("1")` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:216:9 + --> tests/ui/match_single_binding.rs:215:9 | LL | / match 1 { LL | | @@ -319,7 +319,7 @@ LL | | }, | |_________^ help: consider using the match body instead: `()` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:220:9 + --> tests/ui/match_single_binding.rs:219:9 | LL | / match 1 { LL | | @@ -328,7 +328,7 @@ LL | | }, | |_________^ help: consider using the match body instead: `side_effects()` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:224:9 + --> tests/ui/match_single_binding.rs:223:9 | LL | / match 1 { LL | | @@ -337,7 +337,7 @@ LL | | }, | |_________^ help: consider using the match body instead: `println!("1")` error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding.rs:239:5 + --> tests/ui/match_single_binding.rs:238:5 | LL | / match dbg!(3) { LL | | _ => println!("here"), @@ -351,7 +351,7 @@ LL + println!("here"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:243:5 + --> tests/ui/match_single_binding.rs:242:5 | LL | / match dbg!(3) { LL | | id!(a) => println!("found {a}"), @@ -365,7 +365,7 @@ LL + println!("found {a}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:247:5 + --> tests/ui/match_single_binding.rs:246:5 | LL | / let id!(_a) = match dbg!(3) { LL | | id!(b) => dbg!(b + 1), @@ -379,7 +379,7 @@ LL + let id!(_a) = dbg!(b + 1); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:255:21 + --> tests/ui/match_single_binding.rs:254:21 | LL | inner: [(); match 1 { | _____________________^ @@ -397,7 +397,7 @@ LL ~ }], | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:263:13 + --> tests/ui/match_single_binding.rs:262:13 | LL | / match 1 { LL | | @@ -412,11 +412,11 @@ LL + 42 | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:274:9 + --> tests/ui/match_single_binding.rs:273:9 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_________^ | @@ -424,61 +424,61 @@ help: consider using a `let` statement | LL ~ { LL + let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); LL + } | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:282:9 + --> tests/ui/match_single_binding.rs:281:9 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_________^ | help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z) +LL + println!("{x} {y} {z}") | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:290:9 + --> tests/ui/match_single_binding.rs:289:9 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_________^ | help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:300:9 + --> tests/ui/match_single_binding.rs:299:9 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {x} {y}"), LL | | } | |_________^ | help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {x} {y}"); | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:310:13 + --> tests/ui/match_single_binding.rs:309:13 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_____________^ | @@ -486,27 +486,27 @@ help: consider using a `let` statement | LL ~ { LL + let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); LL + } | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding.rs:320:13 + --> tests/ui/match_single_binding.rs:319:13 | LL | / match (a, b, c) { LL | | -LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | (x, y, z) => println!("{x} {y} {z}"), LL | | } | |_____________^ | help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); -LL + println!("{} {} {}", x, y, z); +LL + println!("{x} {y} {z}"); | error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:335:12 + --> tests/ui/match_single_binding.rs:334:12 | LL | && match b { | ____________^ @@ -516,7 +516,7 @@ LL | | }; | |_________^ help: consider using the match body instead: `b < c` error: this match could be replaced by its body itself - --> tests/ui/match_single_binding.rs:341:12 + --> tests/ui/match_single_binding.rs:340:12 | LL | && match (a, b) { | ____________^ diff --git a/tests/ui/match_single_binding2.fixed b/tests/ui/match_single_binding2.fixed index 988121f50d0fa..f00987470ae1b 100644 --- a/tests/ui/match_single_binding2.fixed +++ b/tests/ui/match_single_binding2.fixed @@ -1,6 +1,5 @@ #![warn(clippy::match_single_binding)] #![allow(unused_variables)] -#![allow(clippy::uninlined_format_args)] fn main() { // Lint (additional curly braces needed, see #6572) @@ -29,7 +28,7 @@ fn main() { #[rustfmt::skip] Some((first, _second)) => { let (a, b) = get_tup(); - println!("a {:?} and b {:?}", a, b) + println!("a {a:?} and b {b:?}") }, None => println!("nothing"), } diff --git a/tests/ui/match_single_binding2.rs b/tests/ui/match_single_binding2.rs index a4fb2bd6f3817..5416f647b4e6c 100644 --- a/tests/ui/match_single_binding2.rs +++ b/tests/ui/match_single_binding2.rs @@ -1,6 +1,5 @@ #![warn(clippy::match_single_binding)] #![allow(unused_variables)] -#![allow(clippy::uninlined_format_args)] fn main() { // Lint (additional curly braces needed, see #6572) @@ -30,7 +29,7 @@ fn main() { Some((first, _second)) => { match get_tup() { //~^ match_single_binding - (a, b) => println!("a {:?} and b {:?}", a, b), + (a, b) => println!("a {a:?} and b {b:?}"), } }, None => println!("nothing"), diff --git a/tests/ui/match_single_binding2.stderr b/tests/ui/match_single_binding2.stderr index a24cbe3eed766..65b8aa6acd5e1 100644 --- a/tests/ui/match_single_binding2.stderr +++ b/tests/ui/match_single_binding2.stderr @@ -1,5 +1,5 @@ error: this match could be written as a `let` statement - --> tests/ui/match_single_binding2.rs:17:36 + --> tests/ui/match_single_binding2.rs:16:36 | LL | Some((iter, _item)) => match iter.size_hint() { | ____________________________________^ @@ -19,22 +19,22 @@ LL ~ }, | error: this match could be written as a `let` statement - --> tests/ui/match_single_binding2.rs:31:13 + --> tests/ui/match_single_binding2.rs:30:13 | LL | / match get_tup() { LL | | -LL | | (a, b) => println!("a {:?} and b {:?}", a, b), +LL | | (a, b) => println!("a {a:?} and b {b:?}"), LL | | } | |_____________^ | help: consider using a `let` statement | LL ~ let (a, b) = get_tup(); -LL + println!("a {:?} and b {:?}", a, b) +LL + println!("a {a:?} and b {b:?}") | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding2.rs:43:5 + --> tests/ui/match_single_binding2.rs:42:5 | LL | / match side_effects() { LL | | @@ -49,7 +49,7 @@ LL + println!("Side effects"); | error: this match could be replaced by its scrutinee and body - --> tests/ui/match_single_binding2.rs:51:5 + --> tests/ui/match_single_binding2.rs:50:5 | LL | / match match x { LL | | diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace.fixed index 870ef23113a2f..94ad1aad3eb75 100644 --- a/tests/ui/mem_replace.fixed +++ b/tests/ui/mem_replace.fixed @@ -179,3 +179,9 @@ fn mem_replace_option_with_some_bad_msrv() { let mut an_option = Some(0); let replaced = mem::replace(&mut an_option, Some(1)); } + +fn issue15785() { + let mut text = String::from("foo"); + let replaced = std::mem::take(dbg!(&mut text)); + //~^ mem_replace_with_default +} diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace.rs index b4ed5eafea953..ac79660f0f1ee 100644 --- a/tests/ui/mem_replace.rs +++ b/tests/ui/mem_replace.rs @@ -179,3 +179,9 @@ fn mem_replace_option_with_some_bad_msrv() { let mut an_option = Some(0); let replaced = mem::replace(&mut an_option, Some(1)); } + +fn issue15785() { + let mut text = String::from("foo"); + let replaced = std::mem::replace(dbg!(&mut text), String::default()); + //~^ mem_replace_with_default +} diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr index fb4a367266d30..104c985400282 100644 --- a/tests/ui/mem_replace.stderr +++ b/tests/ui/mem_replace.stderr @@ -181,5 +181,11 @@ error: replacing an `Option` with `Some(..)` LL | let replaced = mem::replace(if b { &mut opt1 } else { &mut opt2 }, Some(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::replace()` instead: `(if b { &mut opt1 } else { &mut opt2 }).replace(1)` -error: aborting due to 29 previous errors +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> tests/ui/mem_replace.rs:185:20 + | +LL | let replaced = std::mem::replace(dbg!(&mut text), String::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(dbg!(&mut text))` + +error: aborting due to 30 previous errors diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index f73fe288b0f8c..9595888b99f83 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -135,6 +135,26 @@ fn filter_next() { let _ = foo.filter(42).next(); } +#[rustfmt::skip] +fn filter_next_back() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + + // Multi-line case. + let _ = v.iter().filter(|&x| { + //~^ filter_next + *x < 0 + } + ).next_back(); + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next_back(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next_back(); +} + fn main() { filter_next(); + filter_next_back(); } diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index b226ce7c65dab..45efea4ee01cd 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -24,5 +24,16 @@ LL | | ).next(); = note: `-D clippy::filter-next` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` -error: aborting due to 2 previous errors +error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead + --> tests/ui/methods.rs:143:13 + | +LL | let _ = v.iter().filter(|&x| { + | _____________^ +LL | | +LL | | *x < 0 +LL | | } +LL | | ).next_back(); + | |________________________________^ + +error: aborting due to 3 previous errors diff --git a/tests/ui/methods_fixable.fixed b/tests/ui/methods_fixable.fixed index 49730d8115584..287d8d881ec28 100644 --- a/tests/ui/methods_fixable.fixed +++ b/tests/ui/methods_fixable.fixed @@ -8,4 +8,18 @@ fn main() { // Single-line case. let _ = v.iter().find(|&x| *x < 0); //~^ filter_next + + let _ = v.iter().rfind(|&x| *x < 0); + //~^ filter_next +} + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + let _ = vec![1].into_iter().rfind(|&x| x < 0); + //~^ filter_next +} + +#[clippy::msrv = "1.26"] +fn msrv_1_26() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); } diff --git a/tests/ui/methods_fixable.rs b/tests/ui/methods_fixable.rs index a499b63b6f5bd..11ce1b63560db 100644 --- a/tests/ui/methods_fixable.rs +++ b/tests/ui/methods_fixable.rs @@ -8,4 +8,18 @@ fn main() { // Single-line case. let _ = v.iter().filter(|&x| *x < 0).next(); //~^ filter_next + + let _ = v.iter().filter(|&x| *x < 0).next_back(); + //~^ filter_next +} + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); + //~^ filter_next +} + +#[clippy::msrv = "1.26"] +fn msrv_1_26() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); } diff --git a/tests/ui/methods_fixable.stderr b/tests/ui/methods_fixable.stderr index 852e7a224a6e3..d26b5e9ac271e 100644 --- a/tests/ui/methods_fixable.stderr +++ b/tests/ui/methods_fixable.stderr @@ -7,5 +7,17 @@ LL | let _ = v.iter().filter(|&x| *x < 0).next(); = note: `-D clippy::filter-next` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` -error: aborting due to 1 previous error +error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead + --> tests/ui/methods_fixable.rs:12:13 + | +LL | let _ = v.iter().filter(|&x| *x < 0).next_back(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `v.iter().rfind(|&x| *x < 0)` + +error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead + --> tests/ui/methods_fixable.rs:18:13 + | +LL | let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec![1].into_iter().rfind(|&x| x < 0)` + +error: aborting due to 3 previous errors diff --git a/tests/ui/module_inception.rs b/tests/ui/module_inception.rs index 15b7fb8777700..5735dd5867d55 100644 --- a/tests/ui/module_inception.rs +++ b/tests/ui/module_inception.rs @@ -38,4 +38,13 @@ mod bar { mod bar {} } +mod with_inner_impl { + struct S; + impl S { + fn f() { + mod with_inner_impl {} + } + } +} + fn main() {} diff --git a/tests/ui/mut_mut.fixed b/tests/ui/mut_mut.fixed new file mode 100644 index 0000000000000..f9a7f5dcb5a15 --- /dev/null +++ b/tests/ui/mut_mut.fixed @@ -0,0 +1,92 @@ +//@aux-build:proc_macros.rs + +#![warn(clippy::mut_mut)] +#![allow(unused)] +#![allow( + clippy::no_effect, + clippy::uninlined_format_args, + clippy::unnecessary_operation, + clippy::needless_pass_by_ref_mut +)] + +extern crate proc_macros; +use proc_macros::{external, inline_macros}; + +fn fun(x: &mut u32) { + //~^ mut_mut +} + +fn less_fun(x: *mut *mut u32) { + let y = x; +} + +macro_rules! mut_ptr { + ($p:expr) => { + &mut $p + }; +} + +#[allow(unused_mut, unused_variables)] +#[inline_macros] +fn main() { + let mut x = &mut 1u32; + //~^ mut_mut + { + let mut y = &mut *x; + //~^ mut_mut + } + + { + let y: &mut u32 = &mut 2; + //~^ mut_mut + //~| mut_mut + } + + { + let y: &mut u32 = &mut 2; + //~^ mut_mut + //~| mut_mut + } + + let mut z = inline!(&mut $(&mut 3u32)); +} + +fn issue939() { + let array = [5, 6, 7, 8, 9]; + let mut args = array.iter().skip(2); + for &arg in &mut args { + println!("{}", arg); + } + + let args = &mut args; + for arg in args { + println!(":{}", arg); + } +} + +fn issue6922() { + // do not lint from an external macro + external!(let mut_mut_ty: &mut &mut u32 = &mut &mut 1u32;); +} + +mod issue9035 { + use std::fmt::Display; + + struct Foo<'a> { + inner: &'a mut dyn Display, + } + + impl Foo<'_> { + fn foo(&mut self) { + let hlp = &mut self.inner; + bar(hlp); + } + } + + fn bar(_: &mut impl Display) {} +} + +fn allow_works() { + #[allow(clippy::mut_mut)] + let _ = &mut &mut 1; +} diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index bbcdbc89b6a4b..bbec48011a17e 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -12,9 +12,8 @@ extern crate proc_macros; use proc_macros::{external, inline_macros}; -fn fun(x: &mut &mut u32) -> bool { +fn fun(x: &mut &mut u32) { //~^ mut_mut - **x > 0 } fn less_fun(x: *mut *mut u32) { @@ -37,23 +36,19 @@ fn main() { //~^ mut_mut } - if fun(x) { + { let y: &mut &mut u32 = &mut &mut 2; //~^ mut_mut //~| mut_mut - **y + **x; } - if fun(x) { + { let y: &mut &mut &mut u32 = &mut &mut &mut 2; //~^ mut_mut //~| mut_mut - //~| mut_mut - ***y + **x; } let mut z = inline!(&mut $(&mut 3u32)); - //~^ mut_mut } fn issue939() { diff --git a/tests/ui/mut_mut.stderr b/tests/ui/mut_mut.stderr index 74b0c9ba145a9..85e9c5649b89a 100644 --- a/tests/ui/mut_mut.stderr +++ b/tests/ui/mut_mut.stderr @@ -1,61 +1,47 @@ -error: generally you want to avoid `&mut &mut _` if possible +error: a type of form `&mut &mut _` --> tests/ui/mut_mut.rs:15:11 | -LL | fn fun(x: &mut &mut u32) -> bool { - | ^^^^^^^^^^^^^ +LL | fn fun(x: &mut &mut u32) { + | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` | = note: `-D clippy::mut-mut` implied by `-D warnings` = 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 +error: an expression of form `&mut &mut _` + --> tests/ui/mut_mut.rs:32:17 | LL | let mut x = &mut &mut 1u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 1u32` -error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:55:25 - | -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:36:21 +error: this expression mutably borrows a mutable reference + --> tests/ui/mut_mut.rs:35:21 | LL | let mut y = &mut x; - | ^^^^^^ + | ^^^^^^ help: reborrow instead: `&mut *x` -error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:41:32 +error: an expression of form `&mut &mut _` + --> tests/ui/mut_mut.rs:40:32 | LL | let y: &mut &mut u32 = &mut &mut 2; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 2` -error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:41:16 +error: a type of form `&mut &mut _` + --> tests/ui/mut_mut.rs:40: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:48:37 - | -LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` -error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:48:16 +error: an expression of form `&mut &mut _` + --> tests/ui/mut_mut.rs:46:37 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut 2` -error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:48:21 +error: a type of form `&mut &mut _` + --> tests/ui/mut_mut.rs:46:16 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut u32` -error: aborting due to 9 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/mut_mut_unfixable.rs b/tests/ui/mut_mut_unfixable.rs new file mode 100644 index 0000000000000..271cb7b968898 --- /dev/null +++ b/tests/ui/mut_mut_unfixable.rs @@ -0,0 +1,42 @@ +//@no-rustfix + +#![warn(clippy::mut_mut)] +#![allow(unused)] +#![expect(clippy::no_effect)] + +//! removing the extra `&mut`s will break the derefs + +fn fun(x: &mut &mut u32) -> bool { + //~^ mut_mut + **x > 0 +} + +fn main() { + let mut x = &mut &mut 1u32; + //~^ mut_mut + { + let mut y = &mut x; + //~^ mut_mut + ***y + **x; + } + + if fun(x) { + let y = &mut &mut 2; + //~^ mut_mut + **y + **x; + } + + if fun(x) { + let y = &mut &mut &mut 2; + //~^ mut_mut + ***y + **x; + } + + if fun(x) { + // The lint will remove the extra `&mut`, but the result will still be a `&mut` of an expr + // of type `&mut _` (x), so the lint will fire again. That's because we've decided that + // doing both fixes in one run is not worth it, given how improbable code like this is. + let y = &mut &mut x; + //~^ mut_mut + } +} diff --git a/tests/ui/mut_mut_unfixable.stderr b/tests/ui/mut_mut_unfixable.stderr new file mode 100644 index 0000000000000..cf66eb2ed1eca --- /dev/null +++ b/tests/ui/mut_mut_unfixable.stderr @@ -0,0 +1,41 @@ +error: a type of form `&mut &mut _` + --> tests/ui/mut_mut_unfixable.rs:9:11 + | +LL | fn fun(x: &mut &mut u32) -> bool { + | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` + | + = note: `-D clippy::mut-mut` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` + +error: an expression of form `&mut &mut _` + --> tests/ui/mut_mut_unfixable.rs:15:17 + | +LL | let mut x = &mut &mut 1u32; + | ^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 1u32` + +error: this expression mutably borrows a mutable reference + --> tests/ui/mut_mut_unfixable.rs:18:21 + | +LL | let mut y = &mut x; + | ^^^^^^ help: reborrow instead: `&mut *x` + +error: an expression of form `&mut &mut _` + --> tests/ui/mut_mut_unfixable.rs:24:17 + | +LL | let y = &mut &mut 2; + | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 2` + +error: an expression of form `&mut &mut _` + --> tests/ui/mut_mut_unfixable.rs:30:17 + | +LL | let y = &mut &mut &mut 2; + | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut 2` + +error: an expression of form `&mut &mut _` + --> tests/ui/mut_mut_unfixable.rs:39:17 + | +LL | let y = &mut &mut x; + | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut x` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/mut_reference.stderr b/tests/ui/mut_reference.stderr deleted file mode 100644 index 5ecfaa37416ba..0000000000000 --- a/tests/ui/mut_reference.stderr +++ /dev/null @@ -1,77 +0,0 @@ -error: the function `takes_ref` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:56:15 - | -LL | takes_ref(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` - | - = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::unnecessary_mut_passed)]` - -error: the function `takes_ref_ref` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:58:19 - | -LL | takes_ref_ref(&mut &42); - | ^^^^^^^^ help: remove this `mut`: `&&42` - -error: the function `takes_ref_refmut` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:60:22 - | -LL | takes_ref_refmut(&mut &mut 42); - | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` - -error: the function `takes_raw_const` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:62:21 - | -LL | takes_raw_const(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` - -error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:66:12 - | -LL | as_ptr(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` - -error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:69:12 - | -LL | as_ptr(&mut &42); - | ^^^^^^^^ help: remove this `mut`: `&&42` - -error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:72:12 - | -LL | as_ptr(&mut &mut 42); - | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` - -error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:75:12 - | -LL | as_ptr(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` - -error: the method `takes_ref` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:80:25 - | -LL | my_struct.takes_ref(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` - -error: the method `takes_ref_ref` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:82:29 - | -LL | my_struct.takes_ref_ref(&mut &42); - | ^^^^^^^^ help: remove this `mut`: `&&42` - -error: the method `takes_ref_refmut` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:84:32 - | -LL | my_struct.takes_ref_refmut(&mut &mut 42); - | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` - -error: the method `takes_raw_const` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:86:31 - | -LL | my_struct.takes_raw_const(&mut 42); - | ^^^^^^^ help: remove this `mut`: `&42` - -error: aborting due to 12 previous errors - diff --git a/tests/ui/new_without_default.fixed b/tests/ui/new_without_default.fixed index 277c335cd8851..9a5e90b48065c 100644 --- a/tests/ui/new_without_default.fixed +++ b/tests/ui/new_without_default.fixed @@ -1,5 +1,4 @@ #![allow( - dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes, clippy::extra_unused_type_parameters, @@ -322,3 +321,91 @@ where Self { _kv: None } } } + +// From issue #14552, but with `#[cfg]`s that are actually `true` in the uitest context + +pub struct NewWithCfg; +#[cfg(not(test))] +impl Default for NewWithCfg { + fn default() -> Self { + Self::new() + } +} + +impl NewWithCfg { + #[cfg(not(test))] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWith2Cfgs; +#[cfg(not(test))] +#[cfg(panic = "unwind")] +impl Default for NewWith2Cfgs { + fn default() -> Self { + Self::new() + } +} + +impl NewWith2Cfgs { + #[cfg(not(test))] + #[cfg(panic = "unwind")] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWithExtraneous; +impl Default for NewWithExtraneous { + fn default() -> Self { + Self::new() + } +} + +impl NewWithExtraneous { + #[inline] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWithCfgAndExtraneous; +#[cfg(not(test))] +impl Default for NewWithCfgAndExtraneous { + fn default() -> Self { + Self::new() + } +} + +impl NewWithCfgAndExtraneous { + #[cfg(not(test))] + #[inline] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +mod issue15778 { + pub struct Foo(Vec); + + impl Foo { + pub fn new() -> Self { + Self(Vec::new()) + } + } + + impl<'a> IntoIterator for &'a Foo { + type Item = &'a i32; + + type IntoIter = std::slice::Iter<'a, i32>; + + fn into_iter(self) -> Self::IntoIter { + self.0.as_slice().iter() + } + } +} diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index f2844897c93d9..f7466aa321896 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -1,5 +1,4 @@ #![allow( - dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes, clippy::extra_unused_type_parameters, @@ -265,3 +264,63 @@ where Self { _kv: None } } } + +// From issue #14552, but with `#[cfg]`s that are actually `true` in the uitest context + +pub struct NewWithCfg; +impl NewWithCfg { + #[cfg(not(test))] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWith2Cfgs; +impl NewWith2Cfgs { + #[cfg(not(test))] + #[cfg(panic = "unwind")] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWithExtraneous; +impl NewWithExtraneous { + #[inline] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +pub struct NewWithCfgAndExtraneous; +impl NewWithCfgAndExtraneous { + #[cfg(not(test))] + #[inline] + pub fn new() -> Self { + //~^ new_without_default + unimplemented!() + } +} + +mod issue15778 { + pub struct Foo(Vec); + + impl Foo { + pub fn new() -> Self { + Self(Vec::new()) + } + } + + impl<'a> IntoIterator for &'a Foo { + type Item = &'a i32; + + type IntoIter = std::slice::Iter<'a, i32>; + + fn into_iter(self) -> Self::IntoIter { + self.0.as_slice().iter() + } + } +} diff --git a/tests/ui/new_without_default.stderr b/tests/ui/new_without_default.stderr index 70a65aba464b8..1e0d5e2131994 100644 --- a/tests/ui/new_without_default.stderr +++ b/tests/ui/new_without_default.stderr @@ -1,5 +1,5 @@ error: you should consider adding a `Default` implementation for `Foo` - --> tests/ui/new_without_default.rs:13:5 + --> tests/ui/new_without_default.rs:12:5 | LL | / pub fn new() -> Foo { LL | | @@ -20,7 +20,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Bar` - --> tests/ui/new_without_default.rs:23:5 + --> tests/ui/new_without_default.rs:22:5 | LL | / pub fn new() -> Self { LL | | @@ -39,7 +39,7 @@ LL + } | error: you should consider adding a `Default` implementation for `LtKo<'c>` - --> tests/ui/new_without_default.rs:89:5 + --> tests/ui/new_without_default.rs:88:5 | LL | / pub fn new() -> LtKo<'c> { LL | | @@ -58,7 +58,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Const` - --> tests/ui/new_without_default.rs:123:5 + --> tests/ui/new_without_default.rs:122:5 | LL | / pub const fn new() -> Const { LL | | @@ -76,7 +76,7 @@ LL + } | error: you should consider adding a `Default` implementation for `NewNotEqualToDerive` - --> tests/ui/new_without_default.rs:184:5 + --> tests/ui/new_without_default.rs:183:5 | LL | / pub fn new() -> Self { LL | | @@ -95,7 +95,7 @@ LL + } | error: you should consider adding a `Default` implementation for `FooGenerics` - --> tests/ui/new_without_default.rs:194:5 + --> tests/ui/new_without_default.rs:193:5 | LL | / pub fn new() -> Self { LL | | @@ -114,7 +114,7 @@ LL + } | error: you should consider adding a `Default` implementation for `BarGenerics` - --> tests/ui/new_without_default.rs:203:5 + --> tests/ui/new_without_default.rs:202:5 | LL | / pub fn new() -> Self { LL | | @@ -133,7 +133,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Foo` - --> tests/ui/new_without_default.rs:216:9 + --> tests/ui/new_without_default.rs:215:9 | LL | / pub fn new() -> Self { LL | | @@ -154,7 +154,7 @@ LL ~ impl Foo { | error: you should consider adding a `Default` implementation for `MyStruct` - --> tests/ui/new_without_default.rs:263:5 + --> tests/ui/new_without_default.rs:262:5 | LL | / pub fn new() -> Self { LL | | @@ -174,5 +174,84 @@ LL + } LL + } | -error: aborting due to 9 previous errors +error: you should consider adding a `Default` implementation for `NewWithCfg` + --> tests/ui/new_without_default.rs:273:5 + | +LL | / pub fn new() -> Self { +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | +help: try adding this + | +LL + #[cfg(not(test))] +LL + impl Default for NewWithCfg { +LL + fn default() -> Self { +LL + Self::new() +LL + } +LL + } +LL | impl NewWithCfg { + | + +error: you should consider adding a `Default` implementation for `NewWith2Cfgs` + --> tests/ui/new_without_default.rs:283:5 + | +LL | / pub fn new() -> Self { +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | +help: try adding this + | +LL + #[cfg(not(test))] +LL + #[cfg(panic = "unwind")] +LL + impl Default for NewWith2Cfgs { +LL + fn default() -> Self { +LL + Self::new() +LL + } +LL + } +LL | impl NewWith2Cfgs { + | + +error: you should consider adding a `Default` implementation for `NewWithExtraneous` + --> tests/ui/new_without_default.rs:292:5 + | +LL | / pub fn new() -> Self { +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | +help: try adding this + | +LL + impl Default for NewWithExtraneous { +LL + fn default() -> Self { +LL + Self::new() +LL + } +LL + } + | + +error: you should consider adding a `Default` implementation for `NewWithCfgAndExtraneous` + --> tests/ui/new_without_default.rs:302:5 + | +LL | / pub fn new() -> Self { +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | +help: try adding this + | +LL + #[cfg(not(test))] +LL + impl Default for NewWithCfgAndExtraneous { +LL + fn default() -> Self { +LL + Self::new() +LL + } +LL + } +LL | impl NewWithCfgAndExtraneous { + | + +error: aborting due to 13 previous errors diff --git a/tests/ui/only_used_in_recursion.rs b/tests/ui/only_used_in_recursion.rs index 7d6075ba9ea47..d58635969238b 100644 --- a/tests/ui/only_used_in_recursion.rs +++ b/tests/ui/only_used_in_recursion.rs @@ -1,4 +1,5 @@ #![warn(clippy::only_used_in_recursion)] +#![warn(clippy::self_only_used_in_recursion)] //@no-rustfix fn _simple(x: u32) -> u32 { x @@ -74,7 +75,7 @@ impl A { } fn _method_self(&self, flag: usize, a: usize) -> usize { - //~^ only_used_in_recursion + //~^ self_only_used_in_recursion //~| only_used_in_recursion if flag == 0 { 0 } else { self._method_self(flag - 1, a) } diff --git a/tests/ui/only_used_in_recursion.stderr b/tests/ui/only_used_in_recursion.stderr index ca08319e11200..5d1e3e9c50fd9 100644 --- a/tests/ui/only_used_in_recursion.stderr +++ b/tests/ui/only_used_in_recursion.stderr @@ -1,11 +1,11 @@ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:11:27 + --> tests/ui/only_used_in_recursion.rs:12:27 | LL | fn _one_unused(flag: u32, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:14:53 + --> tests/ui/only_used_in_recursion.rs:15:53 | LL | if flag == 0 { 0 } else { _one_unused(flag - 1, a) } | ^ @@ -13,181 +13,183 @@ LL | if flag == 0 { 0 } else { _one_unused(flag - 1, a) } = help: to override `-D warnings` add `#[allow(clippy::only_used_in_recursion)]` error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:17:27 + --> tests/ui/only_used_in_recursion.rs:18:27 | LL | fn _two_unused(flag: u32, a: u32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:21:53 + --> tests/ui/only_used_in_recursion.rs:22:53 | LL | if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:17:35 + --> tests/ui/only_used_in_recursion.rs:18:35 | LL | fn _two_unused(flag: u32, a: u32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:21:56 + --> tests/ui/only_used_in_recursion.rs:22:56 | LL | if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:24:26 + --> tests/ui/only_used_in_recursion.rs:25:26 | LL | fn _with_calc(flag: u32, a: i64) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:30:32 + --> tests/ui/only_used_in_recursion.rs:31:32 | LL | _with_calc(flag - 1, (-a + 10) * 5) | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:39:33 + --> tests/ui/only_used_in_recursion.rs:40:33 | LL | fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:46:38 + --> tests/ui/only_used_in_recursion.rs:47:38 | LL | _used_with_unused(flag - 1, -a, a + b) | ^ ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:39:41 + --> tests/ui/only_used_in_recursion.rs:40:41 | LL | fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:46:45 + --> tests/ui/only_used_in_recursion.rs:47:45 | LL | _used_with_unused(flag - 1, -a, a + b) | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:50:35 + --> tests/ui/only_used_in_recursion.rs:51:35 | LL | fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:57:39 + --> tests/ui/only_used_in_recursion.rs:58:39 | LL | _codependent_unused(flag - 1, a * b, a + b) | ^ ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:50:43 + --> tests/ui/only_used_in_recursion.rs:51:43 | LL | fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:57:43 + --> tests/ui/only_used_in_recursion.rs:58:43 | LL | _codependent_unused(flag - 1, a * b, a + b) | ^ ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:61:30 + --> tests/ui/only_used_in_recursion.rs:62:30 | LL | fn _not_primitive(flag: u32, b: String) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:64:56 + --> tests/ui/only_used_in_recursion.rs:65:56 | LL | if flag == 0 { 0 } else { _not_primitive(flag - 1, b) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:70:29 + --> tests/ui/only_used_in_recursion.rs:71:29 | LL | fn _method(flag: usize, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:73:59 + --> tests/ui/only_used_in_recursion.rs:74:59 | LL | if flag == 0 { 0 } else { Self::_method(flag - 1, a) } | ^ -error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:76:22 +error: `self` is only used in recursion + --> tests/ui/only_used_in_recursion.rs:77:22 | LL | fn _method_self(&self, flag: usize, a: usize) -> usize { | ^^^^ | -note: parameter used here - --> tests/ui/only_used_in_recursion.rs:80:35 +note: `self` used here + --> tests/ui/only_used_in_recursion.rs:81:35 | LL | if flag == 0 { 0 } else { self._method_self(flag - 1, a) } | ^^^^ + = note: `-D clippy::self-only-used-in-recursion` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::self_only_used_in_recursion)]` error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:76:41 + --> tests/ui/only_used_in_recursion.rs:77:41 | LL | fn _method_self(&self, flag: usize, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:80:63 + --> tests/ui/only_used_in_recursion.rs:81:63 | LL | if flag == 0 { 0 } else { self._method_self(flag - 1, a) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:90:26 + --> tests/ui/only_used_in_recursion.rs:91:26 | LL | fn method(flag: u32, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:93:58 + --> tests/ui/only_used_in_recursion.rs:94:58 | LL | if flag == 0 { 0 } else { Self::method(flag - 1, a) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:96:38 + --> tests/ui/only_used_in_recursion.rs:97:38 | LL | fn method_self(&self, flag: u32, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:99:62 + --> tests/ui/only_used_in_recursion.rs:100:62 | LL | if flag == 0 { 0 } else { self.method_self(flag - 1, a) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:124:26 + --> tests/ui/only_used_in_recursion.rs:125:26 | LL | fn method(flag: u32, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:127:58 + --> tests/ui/only_used_in_recursion.rs:128:58 | LL | if flag == 0 { 0 } else { Self::method(flag - 1, a) } | ^ error: parameter is only used in recursion - --> tests/ui/only_used_in_recursion.rs:130:38 + --> tests/ui/only_used_in_recursion.rs:131:38 | LL | fn method_self(&self, flag: u32, a: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: parameter used here - --> tests/ui/only_used_in_recursion.rs:133:62 + --> tests/ui/only_used_in_recursion.rs:134:62 | LL | if flag == 0 { 0 } else { self.method_self(flag - 1, a) } | ^ diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 7a0be97017ebc..386351aa39f51 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -1,5 +1,4 @@ #![warn(clippy::or_fun_call)] -#![allow(dead_code)] #![allow( clippy::borrow_as_ptr, clippy::uninlined_format_args, @@ -479,4 +478,19 @@ fn test_result_and() { //~^ or_fun_call } +#[clippy::msrv = "1.15"] +fn below_msrv(opt: Option, res: Result) { + let _ = opt.unwrap_or_default(); + //~^ unwrap_or_default + let _ = res.unwrap_or_else(|_| Default::default()); + //~^ or_fun_call +} + +#[clippy::msrv = "1.16"] +fn above_msrv(opt: Option, res: Result) { + let _ = opt.unwrap_or_default(); + //~^ unwrap_or_default + let _ = res.unwrap_or_default(); + //~^ unwrap_or_default +} fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 724af606de9cf..e27f9aa65c37a 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -1,5 +1,4 @@ #![warn(clippy::or_fun_call)] -#![allow(dead_code)] #![allow( clippy::borrow_as_ptr, clippy::uninlined_format_args, @@ -479,4 +478,19 @@ fn test_result_and() { //~^ or_fun_call } +#[clippy::msrv = "1.15"] +fn below_msrv(opt: Option, res: Result) { + let _ = opt.unwrap_or(Default::default()); + //~^ unwrap_or_default + let _ = res.unwrap_or(Default::default()); + //~^ or_fun_call +} + +#[clippy::msrv = "1.16"] +fn above_msrv(opt: Option, res: Result) { + let _ = opt.unwrap_or(Default::default()); + //~^ unwrap_or_default + let _ = res.unwrap_or(Default::default()); + //~^ unwrap_or_default +} fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 40b25f91154dd..6bce06ab20eb6 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -1,5 +1,5 @@ error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:53:22 + --> tests/ui/or_fun_call.rs:52:22 | LL | with_constructor.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(make)` @@ -8,7 +8,7 @@ LL | with_constructor.unwrap_or(make()); = help: to override `-D warnings` add `#[allow(clippy::or_fun_call)]` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:57:14 + --> tests/ui/or_fun_call.rs:56:14 | LL | with_new.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` @@ -17,205 +17,205 @@ LL | with_new.unwrap_or(Vec::new()); = help: to override `-D warnings` add `#[allow(clippy::unwrap_or_default)]` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:61:21 + --> tests/ui/or_fun_call.rs:60:21 | LL | with_const_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Vec::with_capacity(12))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:65:14 + --> tests/ui/or_fun_call.rs:64:14 | LL | with_err.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| make())` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:69:19 + --> tests/ui/or_fun_call.rs:68:19 | LL | with_err_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Vec::with_capacity(12))` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:73:24 + --> tests/ui/or_fun_call.rs:72:24 | LL | with_default_trait.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:77:23 + --> tests/ui/or_fun_call.rs:76:23 | LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:97:18 + --> tests/ui/or_fun_call.rs:96:18 | LL | self_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(::default)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:101:18 + --> tests/ui/or_fun_call.rs:100:18 | LL | real_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:105:14 + --> tests/ui/or_fun_call.rs:104:14 | LL | with_vec.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:109:21 + --> tests/ui/or_fun_call.rs:108:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(Foo::new)` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:113:19 + --> tests/ui/or_fun_call.rs:112:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:117:23 + --> tests/ui/or_fun_call.rs:116:23 | LL | map_vec.entry(42).or_insert(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:121:21 + --> tests/ui/or_fun_call.rs:120:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:125:25 + --> tests/ui/or_fun_call.rs:124:25 | LL | btree_vec.entry(42).or_insert(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:129:21 + --> tests/ui/or_fun_call.rs:128:21 | LL | let _ = stringy.unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `ok_or` - --> tests/ui/or_fun_call.rs:134:17 + --> tests/ui/or_fun_call.rs:133:17 | LL | let _ = opt.ok_or(format!("{} world.", hello)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:139:21 + --> tests/ui/or_fun_call.rs:138:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:142:21 + --> tests/ui/or_fun_call.rs:141:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `or` - --> tests/ui/or_fun_call.rs:167:35 + --> tests/ui/or_fun_call.rs:166:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:210:18 + --> tests/ui/or_fun_call.rs:209:18 | LL | None.unwrap_or(ptr_to_ref(s)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:218:14 + --> tests/ui/or_fun_call.rs:217:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:221:14 + --> tests/ui/or_fun_call.rs:220:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:297:25 + --> tests/ui/or_fun_call.rs:296:25 | LL | let _ = Some(4).map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:299:25 + --> tests/ui/or_fun_call.rs:298:25 | LL | let _ = Some(4).map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:302:25 + --> tests/ui/or_fun_call.rs:301:25 | LL | let _ = Some(4).map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| "asd".to_string().len() as i32, f)` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:333:18 + --> tests/ui/or_fun_call.rs:332:18 | LL | with_new.unwrap_or_else(Vec::new); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:337:28 + --> tests/ui/or_fun_call.rs:336:28 | LL | with_default_trait.unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:341:27 + --> tests/ui/or_fun_call.rs:340:27 | LL | with_default_type.unwrap_or_else(u64::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:345:22 + --> tests/ui/or_fun_call.rs:344:22 | LL | real_default.unwrap_or_else(::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:349:23 + --> tests/ui/or_fun_call.rs:348:23 | LL | map.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:353:25 + --> tests/ui/or_fun_call.rs:352:25 | LL | btree.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:357:25 + --> tests/ui/or_fun_call.rs:356:25 | LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:399:17 + --> tests/ui/or_fun_call.rs:398:17 | LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:404:17 + --> tests/ui/or_fun_call.rs:403:17 | LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:409:17 + --> tests/ui/or_fun_call.rs:408:17 | LL | let _ = opt.unwrap_or({ | _________________^ @@ -235,58 +235,82 @@ LL ~ }); | error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:415:17 + --> tests/ui/or_fun_call.rs:414:17 | LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:420:17 + --> tests/ui/or_fun_call.rs:419:17 | LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:427:21 + --> tests/ui/or_fun_call.rs:426:21 | LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:442:19 + --> tests/ui/or_fun_call.rs:441:19 | LL | let _ = x.map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:444:19 + --> tests/ui/or_fun_call.rs:443:19 | LL | let _ = x.map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:447:19 + --> tests/ui/or_fun_call.rs:446:19 | LL | let _ = x.map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| "asd".to_string().len() as i32, f)` error: function call inside of `get_or_insert` - --> tests/ui/or_fun_call.rs:458:15 + --> tests/ui/or_fun_call.rs:457:15 | LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:468:15 + --> tests/ui/or_fun_call.rs:467:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:478:15 + --> tests/ui/or_fun_call.rs:477:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` -error: aborting due to 45 previous errors +error: use of `unwrap_or` to construct default value + --> tests/ui/or_fun_call.rs:483:17 + | +LL | let _ = opt.unwrap_or(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + +error: function call inside of `unwrap_or` + --> tests/ui/or_fun_call.rs:485:17 + | +LL | let _ = res.unwrap_or(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Default::default())` + +error: use of `unwrap_or` to construct default value + --> tests/ui/or_fun_call.rs:491:17 + | +LL | let _ = opt.unwrap_or(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + +error: use of `unwrap_or` to construct default value + --> tests/ui/or_fun_call.rs:493:17 + | +LL | let _ = res.unwrap_or(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` + +error: aborting due to 49 previous errors diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index ac81b324c2049..2c5ee02450389 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -475,3 +475,28 @@ fn issue_15679() -> Result { Ok(0) } + +mod issue14894 { + fn use_after_question_mark(do_something_else: impl Fn() -> Result) -> Result<(), ()> { + let result = do_something_else(); + if let Err(reason) = result { + return Err(reason); + } + drop(result); + + let result = do_something_else(); + let x = result?; + drop(x); + + Ok(()) + } + + #[expect(dropping_copy_types)] + fn use_after_question_mark_but_is_copy(do_something_else: impl Fn() -> Result) -> Result<(), ()> { + let result = do_something_else(); + result?; + drop(result); + + Ok(()) + } +} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index b5866dac6b8f6..b9ff9d1565b2f 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -583,3 +583,35 @@ fn issue_15679() -> Result { Ok(0) } + +mod issue14894 { + fn use_after_question_mark(do_something_else: impl Fn() -> Result) -> Result<(), ()> { + let result = do_something_else(); + if let Err(reason) = result { + return Err(reason); + } + drop(result); + + let result = do_something_else(); + let x = match result { + //~^ question_mark + Ok(v) => v, + Err(e) => return Err(e), + }; + drop(x); + + Ok(()) + } + + #[expect(dropping_copy_types)] + fn use_after_question_mark_but_is_copy(do_something_else: impl Fn() -> Result) -> Result<(), ()> { + let result = do_something_else(); + if let Err(reason) = result { + //~^ question_mark + return Err(reason); + } + drop(result); + + Ok(()) + } +} diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index 1ecd936292e55..9b2896328e665 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -313,5 +313,25 @@ LL | | Err(err) => return Err(<&str as Into>::into(err)), LL | | }; | |_____^ help: try instead: `some_result?` -error: aborting due to 33 previous errors +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:596:17 + | +LL | let x = match result { + | _________________^ +LL | | +LL | | Ok(v) => v, +LL | | Err(e) => return Err(e), +LL | | }; + | |_________^ help: try instead: `result?` + +error: this block may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:609:9 + | +LL | / if let Err(reason) = result { +LL | | +LL | | return Err(reason); +LL | | } + | |_________^ help: replace it with: `result?;` + +error: aborting due to 35 previous errors diff --git a/tests/ui/range.fixed b/tests/ui/range.fixed index 0e951d88091b7..c8e654600045e 100644 --- a/tests/ui/range.fixed +++ b/tests/ui/range.fixed @@ -1,11 +1,23 @@ #![allow(clippy::useless_vec)] #[warn(clippy::range_zip_with_len)] fn main() { - let v1 = vec![1, 2, 3]; - let v2 = vec![4, 5]; + let v1: Vec = vec![1, 2, 3]; + let v2: Vec = vec![4, 5]; let _x = v1.iter().enumerate(); //~^ range_zip_with_len + //~v range_zip_with_len + for (i, e) in v1.iter().enumerate() { + let _: &u64 = e; + let _: usize = i; + } + + //~v range_zip_with_len + v1.iter().enumerate().for_each(|(i, e)| { + let _: &u64 = e; + let _: usize = i; + }); + let _y = v1.iter().zip(0..v2.len()); // No error } diff --git a/tests/ui/range.rs b/tests/ui/range.rs index 5343801647434..352d517eabdd7 100644 --- a/tests/ui/range.rs +++ b/tests/ui/range.rs @@ -1,11 +1,23 @@ #![allow(clippy::useless_vec)] #[warn(clippy::range_zip_with_len)] fn main() { - let v1 = vec![1, 2, 3]; - let v2 = vec![4, 5]; + let v1: Vec = vec![1, 2, 3]; + let v2: Vec = vec![4, 5]; let _x = v1.iter().zip(0..v1.len()); //~^ range_zip_with_len + //~v range_zip_with_len + for (e, i) in v1.iter().zip(0..v1.len()) { + let _: &u64 = e; + let _: usize = i; + } + + //~v range_zip_with_len + v1.iter().zip(0..v1.len()).for_each(|(e, i)| { + let _: &u64 = e; + let _: usize = i; + }); + let _y = v1.iter().zip(0..v2.len()); // No error } diff --git a/tests/ui/range.stderr b/tests/ui/range.stderr index 798ce1842d8b5..b0a852a69fc5a 100644 --- a/tests/ui/range.stderr +++ b/tests/ui/range.stderr @@ -2,10 +2,35 @@ error: using `.zip()` with a range and `.len()` --> tests/ui/range.rs:6:14 | LL | let _x = v1.iter().zip(0..v1.len()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `v1.iter().enumerate()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `v1.iter().enumerate()` | + = note: the order of the element and the index will be swapped = note: `-D clippy::range-zip-with-len` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::range_zip_with_len)]` -error: aborting due to 1 previous error +error: using `.zip()` with a range and `.len()` + --> tests/ui/range.rs:10:19 + | +LL | for (e, i) in v1.iter().zip(0..v1.len()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use + | +LL - for (e, i) in v1.iter().zip(0..v1.len()) { +LL + for (i, e) in v1.iter().enumerate() { + | + +error: using `.zip()` with a range and `.len()` + --> tests/ui/range.rs:16:5 + | +LL | v1.iter().zip(0..v1.len()).for_each(|(e, i)| { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use + | +LL - v1.iter().zip(0..v1.len()).for_each(|(e, i)| { +LL + v1.iter().enumerate().for_each(|(i, e)| { + | + +error: aborting due to 3 previous errors diff --git a/tests/ui/range_unfixable.rs b/tests/ui/range_unfixable.rs new file mode 100644 index 0000000000000..259be23fa1e5a --- /dev/null +++ b/tests/ui/range_unfixable.rs @@ -0,0 +1,14 @@ +//@no-rustfix +#![allow(clippy::useless_vec)] +#[warn(clippy::range_zip_with_len)] +fn main() { + let v1: Vec = vec![1, 2, 3]; + let v2: Vec = vec![4, 5]; + + // Do not autofix, `filter()` would not consume the iterator. + //~v range_zip_with_len + v1.iter().zip(0..v1.len()).filter(|(_, i)| *i < 2).for_each(|(e, i)| { + let _: &u64 = e; + let _: usize = i; + }); +} diff --git a/tests/ui/range_unfixable.stderr b/tests/ui/range_unfixable.stderr new file mode 100644 index 0000000000000..fb03ea2c05f6b --- /dev/null +++ b/tests/ui/range_unfixable.stderr @@ -0,0 +1,12 @@ +error: using `.zip()` with a range and `.len()` + --> tests/ui/range_unfixable.rs:10:5 + | +LL | v1.iter().zip(0..v1.len()).filter(|(_, i)| *i < 2).for_each(|(e, i)| { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `v1.iter().enumerate()` + | + = note: the order of the element and the index will be swapped + = note: `-D clippy::range-zip-with-len` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::range_zip_with_len)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index ff81c64260274..fdd851414746b 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -58,6 +58,7 @@ #![allow(clippy::missing_const_for_thread_local)] #![allow(clippy::recursive_format_impl)] #![allow(unnecessary_transmutes)] +#![allow(clippy::unchecked_time_subtraction)] #![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] @@ -131,6 +132,7 @@ #![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_char` #![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_int_to_float` #![warn(unnecessary_transmutes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` +#![warn(clippy::unchecked_time_subtraction)] //~ ERROR: lint `clippy::unchecked_duration_subtraction` #![warn(undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` #![warn(unknown_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(unused_labels)] //~ ERROR: lint `clippy::unused_label` diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index b5d5d07e639a0..591c8ca53ac22 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -58,6 +58,7 @@ #![allow(clippy::missing_const_for_thread_local)] #![allow(clippy::recursive_format_impl)] #![allow(unnecessary_transmutes)] +#![allow(clippy::unchecked_time_subtraction)] #![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] @@ -131,6 +132,7 @@ #![warn(clippy::transmute_int_to_char)] //~ ERROR: lint `clippy::transmute_int_to_char` #![warn(clippy::transmute_int_to_float)] //~ ERROR: lint `clippy::transmute_int_to_float` #![warn(clippy::transmute_num_to_bytes)] //~ ERROR: lint `clippy::transmute_num_to_bytes` +#![warn(clippy::unchecked_duration_subtraction)] //~ ERROR: lint `clippy::unchecked_duration_subtraction` #![warn(clippy::undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops` #![warn(clippy::unknown_clippy_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(clippy::unused_label)] //~ ERROR: lint `clippy::unused_label` diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 2487dfc8eba44..b54fec8c57949 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:67:9 + --> tests/ui/rename.rs:68:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,436 +8,442 @@ 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:68:9 + --> tests/ui/rename.rs:69: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:69:9 + --> tests/ui/rename.rs:70: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:70:9 + --> tests/ui/rename.rs:71: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:71:9 + --> tests/ui/rename.rs:72: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:72:9 + --> tests/ui/rename.rs:73:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> tests/ui/rename.rs:73:9 + --> tests/ui/rename.rs:74: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:74:9 + --> tests/ui/rename.rs:75: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:75:9 + --> tests/ui/rename.rs:76:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> tests/ui/rename.rs:76:9 + --> tests/ui/rename.rs:77: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:77:9 + --> tests/ui/rename.rs:78: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:78:9 + --> tests/ui/rename.rs:79: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:79:9 + --> tests/ui/rename.rs:80: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:80:9 + --> tests/ui/rename.rs:81:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::double_neg` has been renamed to `double_negations` - --> tests/ui/rename.rs:81:9 + --> tests/ui/rename.rs:82:9 | LL | #![warn(clippy::double_neg)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `double_negations` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> tests/ui/rename.rs:82:9 + --> tests/ui/rename.rs:83: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:83:9 + --> tests/ui/rename.rs:84: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:84:9 + --> tests/ui/rename.rs:85:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> tests/ui/rename.rs:85:9 + --> tests/ui/rename.rs:86:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` - --> tests/ui/rename.rs:86:9 + --> tests/ui/rename.rs:87:9 | LL | #![warn(clippy::filter_map)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` - --> tests/ui/rename.rs:87:9 + --> tests/ui/rename.rs:88:9 | LL | #![warn(clippy::find_map)] | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` - --> tests/ui/rename.rs:88:9 + --> tests/ui/rename.rs:89:9 | LL | #![warn(clippy::fn_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> tests/ui/rename.rs:89:9 + --> tests/ui/rename.rs:90: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:90:9 + --> tests/ui/rename.rs:91: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:91:9 + --> tests/ui/rename.rs:92: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:92:9 + --> tests/ui/rename.rs:93: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:93:9 + --> tests/ui/rename.rs:94: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:94:9 + --> tests/ui/rename.rs:95:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> tests/ui/rename.rs:95:9 + --> tests/ui/rename.rs:96:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching` - --> tests/ui/rename.rs:96:9 + --> tests/ui/rename.rs:97:9 | LL | #![warn(clippy::if_let_redundant_pattern_matching)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> tests/ui/rename.rs:97:9 + --> tests/ui/rename.rs:98: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:98:9 + --> tests/ui/rename.rs:99: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:99:9 + --> tests/ui/rename.rs:100: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:100:9 + --> tests/ui/rename.rs:101:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> tests/ui/rename.rs:101:9 + --> tests/ui/rename.rs:102: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:102:9 + --> tests/ui/rename.rs:103:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_null_ptr_usage` has been renamed to `invalid_null_arguments` - --> tests/ui/rename.rs:103:9 + --> tests/ui/rename.rs:104:9 | LL | #![warn(clippy::invalid_null_ptr_usage)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_null_arguments` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> tests/ui/rename.rs:104:9 + --> tests/ui/rename.rs:105: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:105:9 + --> tests/ui/rename.rs:106: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:106:9 + --> tests/ui/rename.rs:107:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> tests/ui/rename.rs:107:9 + --> tests/ui/rename.rs:108:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:108:9 + --> tests/ui/rename.rs:109:9 | LL | #![warn(clippy::maybe_misused_cfg)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> tests/ui/rename.rs:109:9 + --> tests/ui/rename.rs:110:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:110:9 + --> tests/ui/rename.rs:111:9 | LL | #![warn(clippy::mismatched_target_os)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> tests/ui/rename.rs:111:9 + --> tests/ui/rename.rs:112: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:112:9 + --> tests/ui/rename.rs:113: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:113:9 + --> tests/ui/rename.rs:114: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:114:9 + --> tests/ui/rename.rs:115: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:115:9 + --> tests/ui/rename.rs:116: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:116:9 + --> tests/ui/rename.rs:117:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks` - --> tests/ui/rename.rs:117:9 + --> tests/ui/rename.rs:118:9 | LL | #![warn(clippy::overflow_check_conditional)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> tests/ui/rename.rs:118:9 + --> tests/ui/rename.rs:119: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:119:9 + --> tests/ui/rename.rs:120:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> tests/ui/rename.rs:120:9 + --> tests/ui/rename.rs:121: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:121:9 + --> tests/ui/rename.rs:122: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:122:9 + --> tests/ui/rename.rs:123: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:123:9 + --> tests/ui/rename.rs:124:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` - --> tests/ui/rename.rs:124:9 + --> tests/ui/rename.rs:125:9 | LL | #![warn(clippy::reverse_range_loop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> tests/ui/rename.rs:125:9 + --> tests/ui/rename.rs:126: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:126:9 + --> tests/ui/rename.rs:127:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` - --> tests/ui/rename.rs:127:9 + --> tests/ui/rename.rs:128:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local` - --> tests/ui/rename.rs:128:9 + --> tests/ui/rename.rs:129:9 | LL | #![warn(clippy::thread_local_initializer_can_be_made_const)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> tests/ui/rename.rs:129:9 + --> tests/ui/rename.rs:130:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::transmute_float_to_int` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:130:9 + --> tests/ui/rename.rs:131:9 | LL | #![warn(clippy::transmute_float_to_int)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_int_to_char` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:131:9 + --> tests/ui/rename.rs:132:9 | LL | #![warn(clippy::transmute_int_to_char)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_int_to_float` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:132:9 + --> tests/ui/rename.rs:133:9 | LL | #![warn(clippy::transmute_int_to_float)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` error: lint `clippy::transmute_num_to_bytes` has been renamed to `unnecessary_transmutes` - --> tests/ui/rename.rs:133:9 + --> tests/ui/rename.rs:134:9 | LL | #![warn(clippy::transmute_num_to_bytes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unnecessary_transmutes` +error: lint `clippy::unchecked_duration_subtraction` has been renamed to `clippy::unchecked_time_subtraction` + --> tests/ui/rename.rs:135:9 + | +LL | #![warn(clippy::unchecked_duration_subtraction)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unchecked_time_subtraction` + error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> tests/ui/rename.rs:134:9 + --> tests/ui/rename.rs:136: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:135:9 + --> tests/ui/rename.rs:137: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:136:9 + --> tests/ui/rename.rs:138:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> tests/ui/rename.rs:137:9 + --> tests/ui/rename.rs:139:9 | LL | #![warn(clippy::unwrap_or_else_default)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> tests/ui/rename.rs:138:9 + --> tests/ui/rename.rs:140:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> tests/ui/rename.rs:139:9 + --> tests/ui/rename.rs:141:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` -error: aborting due to 73 previous errors +error: aborting due to 74 previous errors diff --git a/tests/ui/repeat_once.fixed b/tests/ui/repeat_once.fixed index e739e176f0acd..c08d630a32f7f 100644 --- a/tests/ui/repeat_once.fixed +++ b/tests/ui/repeat_once.fixed @@ -10,8 +10,7 @@ fn main() { //~^ repeat_once let b = slice.to_vec(); //~^ repeat_once - let c = "hello".to_string(); - //~^ repeat_once + let c = "hello".repeat(N); let d = "hi".to_string(); //~^ repeat_once let e = s.to_string(); diff --git a/tests/ui/repeat_once.rs b/tests/ui/repeat_once.rs index 89ab94bbaee85..d967fdc466ed7 100644 --- a/tests/ui/repeat_once.rs +++ b/tests/ui/repeat_once.rs @@ -11,7 +11,6 @@ fn main() { let b = slice.repeat(1); //~^ repeat_once let c = "hello".repeat(N); - //~^ repeat_once let d = "hi".repeat(1); //~^ repeat_once let e = s.repeat(1); diff --git a/tests/ui/repeat_once.stderr b/tests/ui/repeat_once.stderr index 3db7a3568f8e6..62dbf7d233759 100644 --- a/tests/ui/repeat_once.stderr +++ b/tests/ui/repeat_once.stderr @@ -14,28 +14,22 @@ LL | let b = slice.repeat(1); | ^^^^^^^^^^^^^^^ help: consider using `.to_vec()` instead: `slice.to_vec()` error: calling `repeat(1)` on str - --> tests/ui/repeat_once.rs:13:13 - | -LL | let c = "hello".repeat(N); - | ^^^^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hello".to_string()` - -error: calling `repeat(1)` on str - --> tests/ui/repeat_once.rs:15:13 + --> tests/ui/repeat_once.rs:14:13 | LL | let d = "hi".repeat(1); | ^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hi".to_string()` error: calling `repeat(1)` on str - --> tests/ui/repeat_once.rs:17:13 + --> tests/ui/repeat_once.rs:16:13 | LL | let e = s.repeat(1); | ^^^^^^^^^^^ help: consider using `.to_string()` instead: `s.to_string()` error: calling `repeat(1)` on a string literal - --> tests/ui/repeat_once.rs:19:13 + --> tests/ui/repeat_once.rs:18:13 | LL | let f = string.repeat(1); | ^^^^^^^^^^^^^^^^ help: consider using `.clone()` instead: `string.clone()` -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/should_impl_trait/method_list_1.stderr b/tests/ui/should_impl_trait/method_list_1.edition2015.stderr similarity index 86% rename from tests/ui/should_impl_trait/method_list_1.stderr rename to tests/ui/should_impl_trait/method_list_1.edition2015.stderr index 5609d6a21a360..0312fa8f04fae 100644 --- a/tests/ui/should_impl_trait/method_list_1.stderr +++ b/tests/ui/should_impl_trait/method_list_1.edition2015.stderr @@ -1,5 +1,5 @@ error: method `add` can be confused for the standard trait method `std::ops::Add::add` - --> tests/ui/should_impl_trait/method_list_1.rs:24:5 + --> tests/ui/should_impl_trait/method_list_1.rs:27:5 | LL | / pub fn add(self, other: T) -> T { LL | | @@ -13,7 +13,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::should_implement_trait)]` error: method `as_mut` can be confused for the standard trait method `std::convert::AsMut::as_mut` - --> tests/ui/should_impl_trait/method_list_1.rs:30:5 + --> tests/ui/should_impl_trait/method_list_1.rs:33:5 | LL | / pub fn as_mut(&mut self) -> &mut T { LL | | @@ -25,7 +25,7 @@ LL | | } = help: consider implementing the trait `std::convert::AsMut` or choosing a less ambiguous method name error: method `as_ref` can be confused for the standard trait method `std::convert::AsRef::as_ref` - --> tests/ui/should_impl_trait/method_list_1.rs:36:5 + --> tests/ui/should_impl_trait/method_list_1.rs:39:5 | LL | / pub fn as_ref(&self) -> &T { LL | | @@ -37,7 +37,7 @@ LL | | } = help: consider implementing the trait `std::convert::AsRef` or choosing a less ambiguous method name error: method `bitand` can be confused for the standard trait method `std::ops::BitAnd::bitand` - --> tests/ui/should_impl_trait/method_list_1.rs:42:5 + --> tests/ui/should_impl_trait/method_list_1.rs:45:5 | LL | / pub fn bitand(self, rhs: T) -> T { LL | | @@ -49,7 +49,7 @@ LL | | } = help: consider implementing the trait `std::ops::BitAnd` or choosing a less ambiguous method name error: method `bitor` can be confused for the standard trait method `std::ops::BitOr::bitor` - --> tests/ui/should_impl_trait/method_list_1.rs:48:5 + --> tests/ui/should_impl_trait/method_list_1.rs:51:5 | LL | / pub fn bitor(self, rhs: Self) -> Self { LL | | @@ -61,7 +61,7 @@ LL | | } = help: consider implementing the trait `std::ops::BitOr` or choosing a less ambiguous method name error: method `bitxor` can be confused for the standard trait method `std::ops::BitXor::bitxor` - --> tests/ui/should_impl_trait/method_list_1.rs:54:5 + --> tests/ui/should_impl_trait/method_list_1.rs:57:5 | LL | / pub fn bitxor(self, rhs: Self) -> Self { LL | | @@ -73,7 +73,7 @@ LL | | } = help: consider implementing the trait `std::ops::BitXor` or choosing a less ambiguous method name error: method `borrow` can be confused for the standard trait method `std::borrow::Borrow::borrow` - --> tests/ui/should_impl_trait/method_list_1.rs:60:5 + --> tests/ui/should_impl_trait/method_list_1.rs:63:5 | LL | / pub fn borrow(&self) -> &str { LL | | @@ -85,7 +85,7 @@ LL | | } = help: consider implementing the trait `std::borrow::Borrow` or choosing a less ambiguous method name error: method `borrow_mut` can be confused for the standard trait method `std::borrow::BorrowMut::borrow_mut` - --> tests/ui/should_impl_trait/method_list_1.rs:66:5 + --> tests/ui/should_impl_trait/method_list_1.rs:69:5 | LL | / pub fn borrow_mut(&mut self) -> &mut str { LL | | @@ -97,7 +97,7 @@ LL | | } = help: consider implementing the trait `std::borrow::BorrowMut` or choosing a less ambiguous method name error: method `clone` can be confused for the standard trait method `std::clone::Clone::clone` - --> tests/ui/should_impl_trait/method_list_1.rs:72:5 + --> tests/ui/should_impl_trait/method_list_1.rs:75:5 | LL | / pub fn clone(&self) -> Self { LL | | @@ -109,7 +109,7 @@ LL | | } = help: consider implementing the trait `std::clone::Clone` or choosing a less ambiguous method name error: method `cmp` can be confused for the standard trait method `std::cmp::Ord::cmp` - --> tests/ui/should_impl_trait/method_list_1.rs:78:5 + --> tests/ui/should_impl_trait/method_list_1.rs:81:5 | LL | / pub fn cmp(&self, other: &Self) -> Self { LL | | @@ -121,7 +121,7 @@ LL | | } = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name error: method `default` can be confused for the standard trait method `std::default::Default::default` - --> tests/ui/should_impl_trait/method_list_1.rs:84:5 + --> tests/ui/should_impl_trait/method_list_1.rs:87:5 | LL | / pub fn default() -> Self { LL | | @@ -133,7 +133,7 @@ LL | | } = help: consider implementing the trait `std::default::Default` or choosing a less ambiguous method name error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref` - --> tests/ui/should_impl_trait/method_list_1.rs:90:5 + --> tests/ui/should_impl_trait/method_list_1.rs:93:5 | LL | / pub fn deref(&self) -> &Self { LL | | @@ -145,7 +145,7 @@ LL | | } = help: consider implementing the trait `std::ops::Deref` or choosing a less ambiguous method name error: method `deref_mut` can be confused for the standard trait method `std::ops::DerefMut::deref_mut` - --> tests/ui/should_impl_trait/method_list_1.rs:96:5 + --> tests/ui/should_impl_trait/method_list_1.rs:99:5 | LL | / pub fn deref_mut(&mut self) -> &mut Self { LL | | @@ -157,7 +157,7 @@ LL | | } = help: consider implementing the trait `std::ops::DerefMut` or choosing a less ambiguous method name error: method `div` can be confused for the standard trait method `std::ops::Div::div` - --> tests/ui/should_impl_trait/method_list_1.rs:102:5 + --> tests/ui/should_impl_trait/method_list_1.rs:105:5 | LL | / pub fn div(self, rhs: Self) -> Self { LL | | @@ -169,7 +169,7 @@ LL | | } = help: consider implementing the trait `std::ops::Div` or choosing a less ambiguous method name error: method `drop` can be confused for the standard trait method `std::ops::Drop::drop` - --> tests/ui/should_impl_trait/method_list_1.rs:108:5 + --> tests/ui/should_impl_trait/method_list_1.rs:111:5 | LL | / pub fn drop(&mut self) { LL | | diff --git a/tests/ui/should_impl_trait/method_list_1.edition2021.stderr b/tests/ui/should_impl_trait/method_list_1.edition2021.stderr new file mode 100644 index 0000000000000..0312fa8f04fae --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_1.edition2021.stderr @@ -0,0 +1,184 @@ +error: method `add` can be confused for the standard trait method `std::ops::Add::add` + --> tests/ui/should_impl_trait/method_list_1.rs:27:5 + | +LL | / pub fn add(self, other: T) -> T { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Add` or choosing a less ambiguous method name + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::should_implement_trait)]` + +error: method `as_mut` can be confused for the standard trait method `std::convert::AsMut::as_mut` + --> tests/ui/should_impl_trait/method_list_1.rs:33:5 + | +LL | / pub fn as_mut(&mut self) -> &mut T { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::convert::AsMut` or choosing a less ambiguous method name + +error: method `as_ref` can be confused for the standard trait method `std::convert::AsRef::as_ref` + --> tests/ui/should_impl_trait/method_list_1.rs:39:5 + | +LL | / pub fn as_ref(&self) -> &T { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::convert::AsRef` or choosing a less ambiguous method name + +error: method `bitand` can be confused for the standard trait method `std::ops::BitAnd::bitand` + --> tests/ui/should_impl_trait/method_list_1.rs:45:5 + | +LL | / pub fn bitand(self, rhs: T) -> T { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitAnd` or choosing a less ambiguous method name + +error: method `bitor` can be confused for the standard trait method `std::ops::BitOr::bitor` + --> tests/ui/should_impl_trait/method_list_1.rs:51:5 + | +LL | / pub fn bitor(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitOr` or choosing a less ambiguous method name + +error: method `bitxor` can be confused for the standard trait method `std::ops::BitXor::bitxor` + --> tests/ui/should_impl_trait/method_list_1.rs:57:5 + | +LL | / pub fn bitxor(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitXor` or choosing a less ambiguous method name + +error: method `borrow` can be confused for the standard trait method `std::borrow::Borrow::borrow` + --> tests/ui/should_impl_trait/method_list_1.rs:63:5 + | +LL | / pub fn borrow(&self) -> &str { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::borrow::Borrow` or choosing a less ambiguous method name + +error: method `borrow_mut` can be confused for the standard trait method `std::borrow::BorrowMut::borrow_mut` + --> tests/ui/should_impl_trait/method_list_1.rs:69:5 + | +LL | / pub fn borrow_mut(&mut self) -> &mut str { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::borrow::BorrowMut` or choosing a less ambiguous method name + +error: method `clone` can be confused for the standard trait method `std::clone::Clone::clone` + --> tests/ui/should_impl_trait/method_list_1.rs:75:5 + | +LL | / pub fn clone(&self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::clone::Clone` or choosing a less ambiguous method name + +error: method `cmp` can be confused for the standard trait method `std::cmp::Ord::cmp` + --> tests/ui/should_impl_trait/method_list_1.rs:81:5 + | +LL | / pub fn cmp(&self, other: &Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name + +error: method `default` can be confused for the standard trait method `std::default::Default::default` + --> tests/ui/should_impl_trait/method_list_1.rs:87:5 + | +LL | / pub fn default() -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::default::Default` or choosing a less ambiguous method name + +error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref` + --> tests/ui/should_impl_trait/method_list_1.rs:93:5 + | +LL | / pub fn deref(&self) -> &Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Deref` or choosing a less ambiguous method name + +error: method `deref_mut` can be confused for the standard trait method `std::ops::DerefMut::deref_mut` + --> tests/ui/should_impl_trait/method_list_1.rs:99:5 + | +LL | / pub fn deref_mut(&mut self) -> &mut Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::DerefMut` or choosing a less ambiguous method name + +error: method `div` can be confused for the standard trait method `std::ops::Div::div` + --> tests/ui/should_impl_trait/method_list_1.rs:105:5 + | +LL | / pub fn div(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Div` or choosing a less ambiguous method name + +error: method `drop` can be confused for the standard trait method `std::ops::Drop::drop` + --> tests/ui/should_impl_trait/method_list_1.rs:111:5 + | +LL | / pub fn drop(&mut self) { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Drop` or choosing a less ambiguous method name + +error: aborting due to 15 previous errors + diff --git a/tests/ui/should_impl_trait/method_list_1.rs b/tests/ui/should_impl_trait/method_list_1.rs index e8de0e04c0c4c..bbb04c0c5aa12 100644 --- a/tests/ui/should_impl_trait/method_list_1.rs +++ b/tests/ui/should_impl_trait/method_list_1.rs @@ -1,3 +1,6 @@ +//@revisions: edition2015 edition2021 +//@[edition2015] edition:2015 +//@[edition2021] edition:2021 #![allow( clippy::missing_errors_doc, clippy::needless_pass_by_value, diff --git a/tests/ui/should_impl_trait/method_list_2.edition2015.stderr b/tests/ui/should_impl_trait/method_list_2.edition2015.stderr new file mode 100644 index 0000000000000..259815908fee0 --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_2.edition2015.stderr @@ -0,0 +1,172 @@ +error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq` + --> tests/ui/should_impl_trait/method_list_2.rs:28:5 + | +LL | / pub fn eq(&self, other: &Self) -> bool { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::cmp::PartialEq` or choosing a less ambiguous method name + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::should_implement_trait)]` + +error: method `from_str` can be confused for the standard trait method `std::str::FromStr::from_str` + --> tests/ui/should_impl_trait/method_list_2.rs:40:5 + | +LL | / pub fn from_str(s: &str) -> Result { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::str::FromStr` or choosing a less ambiguous method name + +error: method `hash` can be confused for the standard trait method `std::hash::Hash::hash` + --> tests/ui/should_impl_trait/method_list_2.rs:46:5 + | +LL | / pub fn hash(&self, state: &mut T) { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name + +error: method `index` can be confused for the standard trait method `std::ops::Index::index` + --> tests/ui/should_impl_trait/method_list_2.rs:52:5 + | +LL | / pub fn index(&self, index: usize) -> &Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name + +error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut` + --> tests/ui/should_impl_trait/method_list_2.rs:58:5 + | +LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name + +error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter` + --> tests/ui/should_impl_trait/method_list_2.rs:64:5 + | +LL | / pub fn into_iter(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name + +error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul` + --> tests/ui/should_impl_trait/method_list_2.rs:70:5 + | +LL | / pub fn mul(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name + +error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg` + --> tests/ui/should_impl_trait/method_list_2.rs:76:5 + | +LL | / pub fn neg(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name + +error: method `next` can be confused for the standard trait method `std::iter::Iterator::next` + --> tests/ui/should_impl_trait/method_list_2.rs:82:5 + | +LL | / pub fn next(&mut self) -> Option { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name + +error: method `not` can be confused for the standard trait method `std::ops::Not::not` + --> tests/ui/should_impl_trait/method_list_2.rs:88:5 + | +LL | / pub fn not(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name + +error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem` + --> tests/ui/should_impl_trait/method_list_2.rs:94:5 + | +LL | / pub fn rem(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name + +error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl` + --> tests/ui/should_impl_trait/method_list_2.rs:100:5 + | +LL | / pub fn shl(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name + +error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr` + --> tests/ui/should_impl_trait/method_list_2.rs:106:5 + | +LL | / pub fn shr(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name + +error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub` + --> tests/ui/should_impl_trait/method_list_2.rs:112:5 + | +LL | / pub fn sub(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name + +error: aborting due to 14 previous errors + diff --git a/tests/ui/should_impl_trait/method_list_2.edition2021.stderr b/tests/ui/should_impl_trait/method_list_2.edition2021.stderr new file mode 100644 index 0000000000000..2f90b61e7a176 --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_2.edition2021.stderr @@ -0,0 +1,184 @@ +error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq` + --> tests/ui/should_impl_trait/method_list_2.rs:28:5 + | +LL | / pub fn eq(&self, other: &Self) -> bool { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::cmp::PartialEq` or choosing a less ambiguous method name + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::should_implement_trait)]` + +error: method `from_iter` can be confused for the standard trait method `std::iter::FromIterator::from_iter` + --> tests/ui/should_impl_trait/method_list_2.rs:34:5 + | +LL | / pub fn from_iter(iter: T) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::FromIterator` or choosing a less ambiguous method name + +error: method `from_str` can be confused for the standard trait method `std::str::FromStr::from_str` + --> tests/ui/should_impl_trait/method_list_2.rs:40:5 + | +LL | / pub fn from_str(s: &str) -> Result { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::str::FromStr` or choosing a less ambiguous method name + +error: method `hash` can be confused for the standard trait method `std::hash::Hash::hash` + --> tests/ui/should_impl_trait/method_list_2.rs:46:5 + | +LL | / pub fn hash(&self, state: &mut T) { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name + +error: method `index` can be confused for the standard trait method `std::ops::Index::index` + --> tests/ui/should_impl_trait/method_list_2.rs:52:5 + | +LL | / pub fn index(&self, index: usize) -> &Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name + +error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut` + --> tests/ui/should_impl_trait/method_list_2.rs:58:5 + | +LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name + +error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter` + --> tests/ui/should_impl_trait/method_list_2.rs:64:5 + | +LL | / pub fn into_iter(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name + +error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul` + --> tests/ui/should_impl_trait/method_list_2.rs:70:5 + | +LL | / pub fn mul(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name + +error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg` + --> tests/ui/should_impl_trait/method_list_2.rs:76:5 + | +LL | / pub fn neg(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name + +error: method `next` can be confused for the standard trait method `std::iter::Iterator::next` + --> tests/ui/should_impl_trait/method_list_2.rs:82:5 + | +LL | / pub fn next(&mut self) -> Option { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name + +error: method `not` can be confused for the standard trait method `std::ops::Not::not` + --> tests/ui/should_impl_trait/method_list_2.rs:88:5 + | +LL | / pub fn not(self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name + +error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem` + --> tests/ui/should_impl_trait/method_list_2.rs:94:5 + | +LL | / pub fn rem(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name + +error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl` + --> tests/ui/should_impl_trait/method_list_2.rs:100:5 + | +LL | / pub fn shl(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name + +error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr` + --> tests/ui/should_impl_trait/method_list_2.rs:106:5 + | +LL | / pub fn shr(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name + +error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub` + --> tests/ui/should_impl_trait/method_list_2.rs:112:5 + | +LL | / pub fn sub(self, rhs: Self) -> Self { +LL | | +LL | | +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name + +error: aborting due to 15 previous errors + diff --git a/tests/ui/should_impl_trait/method_list_2.rs b/tests/ui/should_impl_trait/method_list_2.rs index 1f25ab3938a3d..4dfbe7e0f9f4d 100644 --- a/tests/ui/should_impl_trait/method_list_2.rs +++ b/tests/ui/should_impl_trait/method_list_2.rs @@ -1,3 +1,6 @@ +//@revisions: edition2015 edition2021 +//@[edition2015] edition:2015 +//@[edition2021] edition:2021 #![allow( clippy::missing_errors_doc, clippy::needless_pass_by_value, @@ -29,7 +32,7 @@ impl T { } pub fn from_iter(iter: T) -> Self { - //~^ should_implement_trait + //~[edition2021]^ should_implement_trait unimplemented!() } diff --git a/tests/ui/single_match.fixed b/tests/ui/single_match.fixed index db5107600ee6d..03982b069e675 100644 --- a/tests/ui/single_match.fixed +++ b/tests/ui/single_match.fixed @@ -218,7 +218,7 @@ fn main() { }; } -fn issue_10808(bar: Option) { +fn issue10808(bar: Option) { if let Some(v) = bar { unsafe { let r = &v as *const i32; println!("{}", *r); @@ -330,6 +330,7 @@ pub struct Data([u8; 4]); const DATA: Data = Data([1, 2, 3, 4]); const CONST_I32: i32 = 1; +// https://github.com/rust-lang/rust-clippy/issues/13012 fn irrefutable_match() { println!(); //~^^^^ single_match @@ -367,7 +368,7 @@ fn irrefutable_match() { //~| NOTE: you might want to preserve the comments from inside the `match` } -fn issue_14493() { +fn issue14493() { macro_rules! mac { (some) => { Some(42) diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index a367b94c4ca6b..e28128e35adab 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -269,7 +269,7 @@ fn main() { }; } -fn issue_10808(bar: Option) { +fn issue10808(bar: Option) { match bar { Some(v) => unsafe { let r = &v as *const i32; @@ -397,6 +397,7 @@ pub struct Data([u8; 4]); const DATA: Data = Data([1, 2, 3, 4]); const CONST_I32: i32 = 1; +// https://github.com/rust-lang/rust-clippy/issues/13012 fn irrefutable_match() { match DATA { DATA => println!(), @@ -462,7 +463,7 @@ fn irrefutable_match() { //~| NOTE: you might want to preserve the comments from inside the `match` } -fn issue_14493() { +fn issue14493() { macro_rules! mac { (some) => { Some(42) diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 1a4edc45c928d..ba8bc5af5a608 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -225,7 +225,7 @@ LL | | } | |_____^ help: try: `if &s[0..3] == b"foo" { println!() }` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:401:5 + --> tests/ui/single_match.rs:402:5 | LL | / match DATA { LL | | DATA => println!(), @@ -234,7 +234,7 @@ LL | | } | |_____^ help: try: `println!();` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:407:5 + --> tests/ui/single_match.rs:408:5 | LL | / match CONST_I32 { LL | | CONST_I32 => println!(), @@ -243,7 +243,7 @@ LL | | } | |_____^ help: try: `println!();` error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:414:5 + --> tests/ui/single_match.rs:415:5 | LL | / match i { LL | | i => { @@ -263,7 +263,7 @@ LL + } | error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:423:5 + --> tests/ui/single_match.rs:424:5 | LL | / match i { LL | | i => {}, @@ -272,7 +272,7 @@ LL | | } | |_____^ help: `match` expression can be removed error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:429:5 + --> tests/ui/single_match.rs:430:5 | LL | / match i { LL | | i => (), @@ -281,7 +281,7 @@ LL | | } | |_____^ help: `match` expression can be removed error: this pattern is irrefutable, `match` is useless - --> tests/ui/single_match.rs:435:5 + --> tests/ui/single_match.rs:436:5 | LL | / match CONST_I32 { LL | | CONST_I32 => println!(), @@ -290,7 +290,7 @@ LL | | } | |_____^ help: try: `println!();` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:443:5 + --> tests/ui/single_match.rs:444:5 | LL | / match x.pop() { LL | | // bla @@ -302,7 +302,7 @@ LL | | } = note: you might want to preserve the comments from inside the `match` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:452:5 + --> tests/ui/single_match.rs:453:5 | LL | / match x.pop() { LL | | // bla @@ -322,7 +322,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:478:5 + --> tests/ui/single_match.rs:479:5 | LL | / match mac!(some) { LL | | Some(u) => println!("{u}"), @@ -331,7 +331,7 @@ LL | | } | |_____^ help: try: `if let Some(u) = mac!(some) { println!("{u}") }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:486:5 + --> tests/ui/single_match.rs:487:5 | LL | / match mac!(str) { LL | | "foo" => println!("eq"), diff --git a/tests/ui/unchecked_duration_subtraction.fixed b/tests/ui/unchecked_duration_subtraction.fixed deleted file mode 100644 index bddffe44ac4d0..0000000000000 --- a/tests/ui/unchecked_duration_subtraction.fixed +++ /dev/null @@ -1,20 +0,0 @@ -#![warn(clippy::unchecked_duration_subtraction)] - -use std::time::{Duration, Instant}; - -fn main() { - let _first = Instant::now(); - let second = Duration::from_secs(3); - - let _ = _first.checked_sub(second).unwrap(); - //~^ unchecked_duration_subtraction - - let _ = Instant::now().checked_sub(Duration::from_secs(5)).unwrap(); - //~^ unchecked_duration_subtraction - - let _ = _first.checked_sub(Duration::from_secs(5)).unwrap(); - //~^ unchecked_duration_subtraction - - let _ = Instant::now().checked_sub(second).unwrap(); - //~^ unchecked_duration_subtraction -} diff --git a/tests/ui/unchecked_duration_subtraction.rs b/tests/ui/unchecked_duration_subtraction.rs deleted file mode 100644 index bb0f712396424..0000000000000 --- a/tests/ui/unchecked_duration_subtraction.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![warn(clippy::unchecked_duration_subtraction)] - -use std::time::{Duration, Instant}; - -fn main() { - let _first = Instant::now(); - let second = Duration::from_secs(3); - - let _ = _first - second; - //~^ unchecked_duration_subtraction - - let _ = Instant::now() - Duration::from_secs(5); - //~^ unchecked_duration_subtraction - - let _ = _first - Duration::from_secs(5); - //~^ unchecked_duration_subtraction - - let _ = Instant::now() - second; - //~^ unchecked_duration_subtraction -} diff --git a/tests/ui/unchecked_duration_subtraction.stderr b/tests/ui/unchecked_duration_subtraction.stderr deleted file mode 100644 index be291c320e689..0000000000000 --- a/tests/ui/unchecked_duration_subtraction.stderr +++ /dev/null @@ -1,29 +0,0 @@ -error: unchecked subtraction of a 'Duration' from an 'Instant' - --> tests/ui/unchecked_duration_subtraction.rs:9:13 - | -LL | let _ = _first - second; - | ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap()` - | - = note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::unchecked_duration_subtraction)]` - -error: unchecked subtraction of a 'Duration' from an 'Instant' - --> tests/ui/unchecked_duration_subtraction.rs:12:13 - | -LL | let _ = Instant::now() - Duration::from_secs(5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap()` - -error: unchecked subtraction of a 'Duration' from an 'Instant' - --> tests/ui/unchecked_duration_subtraction.rs:15:13 - | -LL | let _ = _first - Duration::from_secs(5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap()` - -error: unchecked subtraction of a 'Duration' from an 'Instant' - --> tests/ui/unchecked_duration_subtraction.rs:18:13 - | -LL | let _ = Instant::now() - second; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap()` - -error: aborting due to 4 previous errors - diff --git a/tests/ui/unchecked_time_subtraction.fixed b/tests/ui/unchecked_time_subtraction.fixed new file mode 100644 index 0000000000000..2f923fef4c25d --- /dev/null +++ b/tests/ui/unchecked_time_subtraction.fixed @@ -0,0 +1,37 @@ +#![warn(clippy::unchecked_time_subtraction)] + +use std::time::{Duration, Instant}; + +fn main() { + let _first = Instant::now(); + let second = Duration::from_secs(3); + + let _ = _first.checked_sub(second).unwrap(); + //~^ unchecked_time_subtraction + + let _ = Instant::now().checked_sub(Duration::from_secs(5)).unwrap(); + //~^ unchecked_time_subtraction + + let _ = _first.checked_sub(Duration::from_secs(5)).unwrap(); + //~^ unchecked_time_subtraction + + let _ = Instant::now().checked_sub(second).unwrap(); + //~^ unchecked_time_subtraction + + // Duration - Duration cases + let dur1 = Duration::from_secs(5); + let dur2 = Duration::from_secs(3); + + let _ = dur1.checked_sub(dur2).unwrap(); + //~^ unchecked_time_subtraction + + let _ = Duration::from_secs(10).checked_sub(Duration::from_secs(5)).unwrap(); + //~^ unchecked_time_subtraction + + let _ = second.checked_sub(dur1).unwrap(); + //~^ unchecked_time_subtraction + + // Duration multiplication and subtraction + let _ = (2 * dur1).checked_sub(dur2).unwrap(); + //~^ unchecked_time_subtraction +} diff --git a/tests/ui/unchecked_time_subtraction.rs b/tests/ui/unchecked_time_subtraction.rs new file mode 100644 index 0000000000000..cf727f62aafa7 --- /dev/null +++ b/tests/ui/unchecked_time_subtraction.rs @@ -0,0 +1,37 @@ +#![warn(clippy::unchecked_time_subtraction)] + +use std::time::{Duration, Instant}; + +fn main() { + let _first = Instant::now(); + let second = Duration::from_secs(3); + + let _ = _first - second; + //~^ unchecked_time_subtraction + + let _ = Instant::now() - Duration::from_secs(5); + //~^ unchecked_time_subtraction + + let _ = _first - Duration::from_secs(5); + //~^ unchecked_time_subtraction + + let _ = Instant::now() - second; + //~^ unchecked_time_subtraction + + // Duration - Duration cases + let dur1 = Duration::from_secs(5); + let dur2 = Duration::from_secs(3); + + let _ = dur1 - dur2; + //~^ unchecked_time_subtraction + + let _ = Duration::from_secs(10) - Duration::from_secs(5); + //~^ unchecked_time_subtraction + + let _ = second - dur1; + //~^ unchecked_time_subtraction + + // Duration multiplication and subtraction + let _ = 2 * dur1 - dur2; + //~^ unchecked_time_subtraction +} diff --git a/tests/ui/unchecked_time_subtraction.stderr b/tests/ui/unchecked_time_subtraction.stderr new file mode 100644 index 0000000000000..7a39712269cf7 --- /dev/null +++ b/tests/ui/unchecked_time_subtraction.stderr @@ -0,0 +1,53 @@ +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> tests/ui/unchecked_time_subtraction.rs:9:13 + | +LL | let _ = _first - second; + | ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap()` + | + = note: `-D clippy::unchecked-time-subtraction` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unchecked_time_subtraction)]` + +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> tests/ui/unchecked_time_subtraction.rs:12:13 + | +LL | let _ = Instant::now() - Duration::from_secs(5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap()` + +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> tests/ui/unchecked_time_subtraction.rs:15:13 + | +LL | let _ = _first - Duration::from_secs(5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap()` + +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> tests/ui/unchecked_time_subtraction.rs:18:13 + | +LL | let _ = Instant::now() - second; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap()` + +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction.rs:25:13 + | +LL | let _ = dur1 - dur2; + | ^^^^^^^^^^^ help: try: `dur1.checked_sub(dur2).unwrap()` + +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction.rs:28:13 + | +LL | let _ = Duration::from_secs(10) - Duration::from_secs(5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Duration::from_secs(10).checked_sub(Duration::from_secs(5)).unwrap()` + +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction.rs:31:13 + | +LL | let _ = second - dur1; + | ^^^^^^^^^^^^^ help: try: `second.checked_sub(dur1).unwrap()` + +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction.rs:35:13 + | +LL | let _ = 2 * dur1 - dur2; + | ^^^^^^^^^^^^^^^ help: try: `(2 * dur1).checked_sub(dur2).unwrap()` + +error: aborting due to 8 previous errors + diff --git a/tests/ui/unchecked_time_subtraction_unfixable.rs b/tests/ui/unchecked_time_subtraction_unfixable.rs new file mode 100644 index 0000000000000..4b6a5ca156209 --- /dev/null +++ b/tests/ui/unchecked_time_subtraction_unfixable.rs @@ -0,0 +1,22 @@ +#![warn(clippy::unchecked_time_subtraction)] +//@no-rustfix + +use std::time::{Duration, Instant}; + +fn main() { + let dur1 = Duration::from_secs(5); + let dur2 = Duration::from_secs(3); + let dur3 = Duration::from_secs(1); + + // Chained Duration subtraction - should lint without suggestion due to complexity + let _ = dur1 - dur2 - dur3; + //~^ unchecked_time_subtraction + //~| unchecked_time_subtraction + + // Chained Instant - Duration subtraction - should lint without suggestion due to complexity + let instant1 = Instant::now(); + + let _ = instant1 - dur2 - dur3; + //~^ unchecked_time_subtraction + //~| unchecked_time_subtraction +} diff --git a/tests/ui/unchecked_time_subtraction_unfixable.stderr b/tests/ui/unchecked_time_subtraction_unfixable.stderr new file mode 100644 index 0000000000000..c25c112b06ce0 --- /dev/null +++ b/tests/ui/unchecked_time_subtraction_unfixable.stderr @@ -0,0 +1,29 @@ +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction_unfixable.rs:12:13 + | +LL | let _ = dur1 - dur2 - dur3; + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unchecked-time-subtraction` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unchecked_time_subtraction)]` + +error: unchecked subtraction between 'Duration' values + --> tests/ui/unchecked_time_subtraction_unfixable.rs:12:13 + | +LL | let _ = dur1 - dur2 - dur3; + | ^^^^^^^^^^^ help: try: `dur1.checked_sub(dur2).unwrap()` + +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> tests/ui/unchecked_time_subtraction_unfixable.rs:19:13 + | +LL | let _ = instant1 - dur2 - dur3; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> tests/ui/unchecked_time_subtraction_unfixable.rs:19:13 + | +LL | let _ = instant1 - dur2 - dur3; + | ^^^^^^^^^^^^^^^ help: try: `instant1.checked_sub(dur2).unwrap()` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/unnecessary_clone.rs b/tests/ui/unnecessary_clone.rs deleted file mode 100644 index 7335b6f9f0390..0000000000000 --- a/tests/ui/unnecessary_clone.rs +++ /dev/null @@ -1,111 +0,0 @@ -// does not test any rustfixable lints -#![warn(clippy::clone_on_ref_ptr)] -#![allow(unused)] -#![allow(clippy::redundant_clone, clippy::uninlined_format_args, clippy::unnecessary_wraps)] -//@no-rustfix -use std::cell::RefCell; -use std::rc::{self, Rc}; -use std::sync::{self, Arc}; - -trait SomeTrait {} -struct SomeImpl; -impl SomeTrait for SomeImpl {} - -fn main() {} - -fn clone_on_ref_ptr() { - let rc = Rc::new(true); - let arc = Arc::new(true); - - let rcweak = Rc::downgrade(&rc); - let arc_weak = Arc::downgrade(&arc); - - rc.clone(); - //~^ clone_on_ref_ptr - - Rc::clone(&rc); - - arc.clone(); - //~^ clone_on_ref_ptr - - Arc::clone(&arc); - - rcweak.clone(); - //~^ clone_on_ref_ptr - - rc::Weak::clone(&rcweak); - - arc_weak.clone(); - //~^ clone_on_ref_ptr - - sync::Weak::clone(&arc_weak); - - let x = Arc::new(SomeImpl); - let _: Arc = x.clone(); - //~^ clone_on_ref_ptr -} - -fn clone_on_copy_generic(t: T) { - t.clone(); - //~^ clone_on_copy - - Some(t).clone(); - //~^ clone_on_copy -} - -mod many_derefs { - struct A; - struct B; - struct C; - struct D; - #[derive(Copy, Clone)] - struct E; - - macro_rules! impl_deref { - ($src:ident, $dst:ident) => { - impl std::ops::Deref for $src { - type Target = $dst; - fn deref(&self) -> &Self::Target { - &$dst - } - } - }; - } - - impl_deref!(A, B); - impl_deref!(B, C); - impl_deref!(C, D); - impl std::ops::Deref for D { - type Target = &'static E; - fn deref(&self) -> &Self::Target { - &&E - } - } - - fn go1() { - let a = A; - let _: E = a.clone(); - //~^ clone_on_copy - - let _: E = *****a; - } -} - -mod issue2076 { - use std::rc::Rc; - - macro_rules! try_opt { - ($expr: expr) => { - match $expr { - Some(value) => value, - None => return None, - } - }; - } - - fn func() -> Option> { - let rc = Rc::new(42); - Some(try_opt!(Some(rc)).clone()) - //~^ clone_on_ref_ptr - } -} diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr deleted file mode 100644 index 17518e123d12e..0000000000000 --- a/tests/ui/unnecessary_clone.stderr +++ /dev/null @@ -1,62 +0,0 @@ -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:23:5 - | -LL | rc.clone(); - | ^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&rc)` - | - = note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::clone_on_ref_ptr)]` - -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:28:5 - | -LL | arc.clone(); - | ^^^^^^^^^^^ help: try: `std::sync::Arc::::clone(&arc)` - -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:33:5 - | -LL | rcweak.clone(); - | ^^^^^^^^^^^^^^ help: try: `std::rc::Weak::::clone(&rcweak)` - -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:38:5 - | -LL | arc_weak.clone(); - | ^^^^^^^^^^^^^^^^ help: try: `std::sync::Weak::::clone(&arc_weak)` - -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:44:33 - | -LL | let _: Arc = x.clone(); - | ^^^^^^^^^ help: try: `std::sync::Arc::::clone(&x)` - -error: using `clone` on type `T` which implements the `Copy` trait - --> tests/ui/unnecessary_clone.rs:49:5 - | -LL | t.clone(); - | ^^^^^^^^^ help: try removing the `clone` call: `t` - | - = note: `-D clippy::clone-on-copy` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::clone_on_copy)]` - -error: using `clone` on type `Option` which implements the `Copy` trait - --> tests/ui/unnecessary_clone.rs:52:5 - | -LL | Some(t).clone(); - | ^^^^^^^^^^^^^^^ help: try removing the `clone` call: `Some(t)` - -error: using `clone` on type `E` which implements the `Copy` trait - --> tests/ui/unnecessary_clone.rs:87:20 - | -LL | let _: E = a.clone(); - | ^^^^^^^^^ help: try dereferencing it: `*****a` - -error: using `.clone()` on a ref-counted pointer - --> tests/ui/unnecessary_clone.rs:108:14 - | -LL | Some(try_opt!(Some(rc)).clone()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&try_opt!(Some(rc)))` - -error: aborting due to 9 previous errors - diff --git a/tests/ui/mut_reference.fixed b/tests/ui/unnecessary_mut_passed.fixed similarity index 87% rename from tests/ui/mut_reference.fixed rename to tests/ui/unnecessary_mut_passed.fixed index 03d854099e644..63bbadb01dcb2 100644 --- a/tests/ui/mut_reference.fixed +++ b/tests/ui/unnecessary_mut_passed.fixed @@ -40,6 +40,7 @@ mod issue11268 { struct MyStruct; impl MyStruct { + fn takes_nothing(&self) {} fn takes_ref(&self, a: &i32) {} fn takes_refmut(&self, a: &mut i32) {} fn takes_ref_ref(&self, a: &&i32) {} @@ -168,3 +169,22 @@ fn raw_ptrs(my_struct: MyStruct) { my_struct.takes_raw_mut(&raw mut n); my_struct.takes_raw_const(a); } + +#[expect(clippy::needless_borrow)] +fn issue15722(mut my_struct: MyStruct) { + (&my_struct).takes_nothing(); + //~^ unnecessary_mut_passed + (&my_struct).takes_nothing(); + + // Only put parens around the argument that used to have it + (&my_struct).takes_ref(&42); + //~^ unnecessary_mut_passed + //~| unnecessary_mut_passed + #[expect(clippy::double_parens)] + (&my_struct).takes_ref((&42)); + //~^ unnecessary_mut_passed + //~| unnecessary_mut_passed + #[expect(clippy::double_parens)] + my_struct.takes_ref((&42)); + //~^ unnecessary_mut_passed +} diff --git a/tests/ui/mut_reference.rs b/tests/ui/unnecessary_mut_passed.rs similarity index 87% rename from tests/ui/mut_reference.rs rename to tests/ui/unnecessary_mut_passed.rs index 80e3f50692770..b719ca1871b29 100644 --- a/tests/ui/mut_reference.rs +++ b/tests/ui/unnecessary_mut_passed.rs @@ -40,6 +40,7 @@ mod issue11268 { struct MyStruct; impl MyStruct { + fn takes_nothing(&self) {} fn takes_ref(&self, a: &i32) {} fn takes_refmut(&self, a: &mut i32) {} fn takes_ref_ref(&self, a: &&i32) {} @@ -168,3 +169,22 @@ fn raw_ptrs(my_struct: MyStruct) { my_struct.takes_raw_mut(&raw mut n); my_struct.takes_raw_const(a); } + +#[expect(clippy::needless_borrow)] +fn issue15722(mut my_struct: MyStruct) { + (&mut my_struct).takes_nothing(); + //~^ unnecessary_mut_passed + (&my_struct).takes_nothing(); + + // Only put parens around the argument that used to have it + (&mut my_struct).takes_ref(&mut 42); + //~^ unnecessary_mut_passed + //~| unnecessary_mut_passed + #[expect(clippy::double_parens)] + (&mut my_struct).takes_ref((&mut 42)); + //~^ unnecessary_mut_passed + //~| unnecessary_mut_passed + #[expect(clippy::double_parens)] + my_struct.takes_ref((&mut 42)); + //~^ unnecessary_mut_passed +} diff --git a/tests/ui/unnecessary_mut_passed.stderr b/tests/ui/unnecessary_mut_passed.stderr new file mode 100644 index 0000000000000..ace11027e3e25 --- /dev/null +++ b/tests/ui/unnecessary_mut_passed.stderr @@ -0,0 +1,220 @@ +error: the function `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:57:15 + | +LL | takes_ref(&mut 42); + | ^^^^^^^ + | + = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_mut_passed)]` +help: remove this `mut` + | +LL - takes_ref(&mut 42); +LL + takes_ref(&42); + | + +error: the function `takes_ref_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:59:19 + | +LL | takes_ref_ref(&mut &42); + | ^^^^^^^^ + | +help: remove this `mut` + | +LL - takes_ref_ref(&mut &42); +LL + takes_ref_ref(&&42); + | + +error: the function `takes_ref_refmut` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:61:22 + | +LL | takes_ref_refmut(&mut &mut 42); + | ^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - takes_ref_refmut(&mut &mut 42); +LL + takes_ref_refmut(&&mut 42); + | + +error: the function `takes_raw_const` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:63:21 + | +LL | takes_raw_const(&mut 42); + | ^^^^^^^ + | +help: remove this `mut` + | +LL - takes_raw_const(&mut 42); +LL + takes_raw_const(&42); + | + +error: the function `as_ptr` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:67:12 + | +LL | as_ptr(&mut 42); + | ^^^^^^^ + | +help: remove this `mut` + | +LL - as_ptr(&mut 42); +LL + as_ptr(&42); + | + +error: the function `as_ptr` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:70:12 + | +LL | as_ptr(&mut &42); + | ^^^^^^^^ + | +help: remove this `mut` + | +LL - as_ptr(&mut &42); +LL + as_ptr(&&42); + | + +error: the function `as_ptr` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:73:12 + | +LL | as_ptr(&mut &mut 42); + | ^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - as_ptr(&mut &mut 42); +LL + as_ptr(&&mut 42); + | + +error: the function `as_ptr` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:76:12 + | +LL | as_ptr(&mut 42); + | ^^^^^^^ + | +help: remove this `mut` + | +LL - as_ptr(&mut 42); +LL + as_ptr(&42); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:81:25 + | +LL | my_struct.takes_ref(&mut 42); + | ^^^^^^^ + | +help: remove this `mut` + | +LL - my_struct.takes_ref(&mut 42); +LL + my_struct.takes_ref(&42); + | + +error: the method `takes_ref_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:83:29 + | +LL | my_struct.takes_ref_ref(&mut &42); + | ^^^^^^^^ + | +help: remove this `mut` + | +LL - my_struct.takes_ref_ref(&mut &42); +LL + my_struct.takes_ref_ref(&&42); + | + +error: the method `takes_ref_refmut` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:85:32 + | +LL | my_struct.takes_ref_refmut(&mut &mut 42); + | ^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - my_struct.takes_ref_refmut(&mut &mut 42); +LL + my_struct.takes_ref_refmut(&&mut 42); + | + +error: the method `takes_raw_const` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:87:31 + | +LL | my_struct.takes_raw_const(&mut 42); + | ^^^^^^^ + | +help: remove this `mut` + | +LL - my_struct.takes_raw_const(&mut 42); +LL + my_struct.takes_raw_const(&42); + | + +error: the method `takes_nothing` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:175:5 + | +LL | (&mut my_struct).takes_nothing(); + | ^^^^^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - (&mut my_struct).takes_nothing(); +LL + (&my_struct).takes_nothing(); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:180:5 + | +LL | (&mut my_struct).takes_ref(&mut 42); + | ^^^^^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - (&mut my_struct).takes_ref(&mut 42); +LL + (&my_struct).takes_ref(&mut 42); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:180:32 + | +LL | (&mut my_struct).takes_ref(&mut 42); + | ^^^^^^^ + | +help: remove this `mut` + | +LL - (&mut my_struct).takes_ref(&mut 42); +LL + (&mut my_struct).takes_ref(&42); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:184:5 + | +LL | (&mut my_struct).takes_ref((&mut 42)); + | ^^^^^^^^^^^^^^^^ + | +help: remove this `mut` + | +LL - (&mut my_struct).takes_ref((&mut 42)); +LL + (&my_struct).takes_ref((&mut 42)); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:184:32 + | +LL | (&mut my_struct).takes_ref((&mut 42)); + | ^^^^^^^^^ + | +help: remove this `mut` + | +LL - (&mut my_struct).takes_ref((&mut 42)); +LL + (&mut my_struct).takes_ref((&42)); + | + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/unnecessary_mut_passed.rs:188:25 + | +LL | my_struct.takes_ref((&mut 42)); + | ^^^^^^^^^ + | +help: remove this `mut` + | +LL - my_struct.takes_ref((&mut 42)); +LL + my_struct.takes_ref((&42)); + | + +error: aborting due to 18 previous errors + diff --git a/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.fixed b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.fixed new file mode 100644 index 0000000000000..b90ae1365bbf2 --- /dev/null +++ b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.fixed @@ -0,0 +1,13 @@ +#![warn(clippy::unnecessary_semicolon)] +#![feature(stmt_expr_attributes)] + +fn main() { + // removing the `;` would turn the stmt into an expr, but attrs aren't allowed on exprs + // -- unless the feature `stmt_expr_attributes` is enabled + #[rustfmt::skip] + match 0 { + 0b00 => {} 0b01 => {} + 0b11 => {} _ => {} + } + //~^ unnecessary_semicolon +} diff --git a/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.rs b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.rs new file mode 100644 index 0000000000000..606c901c20d35 --- /dev/null +++ b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.rs @@ -0,0 +1,13 @@ +#![warn(clippy::unnecessary_semicolon)] +#![feature(stmt_expr_attributes)] + +fn main() { + // removing the `;` would turn the stmt into an expr, but attrs aren't allowed on exprs + // -- unless the feature `stmt_expr_attributes` is enabled + #[rustfmt::skip] + match 0 { + 0b00 => {} 0b01 => {} + 0b11 => {} _ => {} + }; + //~^ unnecessary_semicolon +} diff --git a/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.stderr b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.stderr new file mode 100644 index 0000000000000..3e98a92ef299c --- /dev/null +++ b/tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.stderr @@ -0,0 +1,11 @@ +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon_feature_stmt_expr_attributes.rs:11:6 + | +LL | }; + | ^ help: remove + | + = note: `-D clippy::unnecessary-semicolon` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_semicolon)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/zero_repeat_side_effects.fixed b/tests/ui/zero_repeat_side_effects.fixed index fb9d7880a4a7f..e6c451ce7399e 100644 --- a/tests/ui/zero_repeat_side_effects.fixed +++ b/tests/ui/zero_repeat_side_effects.fixed @@ -1,7 +1,5 @@ #![warn(clippy::zero_repeat_side_effects)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::useless_vec)] -#![allow(clippy::needless_late_init)] +#![expect(clippy::unnecessary_operation, clippy::useless_vec, clippy::needless_late_init)] fn f() -> i32 { println!("side effect"); @@ -79,3 +77,27 @@ fn issue_13110() { const LENGTH: usize = LEN!(); let _data = [f(); LENGTH]; } + +// TODO: consider moving the defintion+impl inside `issue_14681` +// once https://github.com/rust-lang/rust/issues/146786 is fixed +#[derive(Clone, Copy)] +struct S; + +impl S { + fn new() -> Self { + println!("This is a side effect"); + S + } +} + +// should not trigger on non-function calls +fn issue_14681() { + fn foo(_s: &[Option]) {} + + foo(&[Some(0i64); 0]); + foo(&[Some(Some(0i64)); 0]); + foo(&{ Some(f()); [] as [std::option::Option; 0] }); + //~^ zero_repeat_side_effects + foo(&{ Some(Some(S::new())); [] as [std::option::Option>; 0] }); + //~^ zero_repeat_side_effects +} diff --git a/tests/ui/zero_repeat_side_effects.rs b/tests/ui/zero_repeat_side_effects.rs index 8b22ff840244e..f8a497976aa43 100644 --- a/tests/ui/zero_repeat_side_effects.rs +++ b/tests/ui/zero_repeat_side_effects.rs @@ -1,7 +1,5 @@ #![warn(clippy::zero_repeat_side_effects)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::useless_vec)] -#![allow(clippy::needless_late_init)] +#![expect(clippy::unnecessary_operation, clippy::useless_vec, clippy::needless_late_init)] fn f() -> i32 { println!("side effect"); @@ -79,3 +77,27 @@ fn issue_13110() { const LENGTH: usize = LEN!(); let _data = [f(); LENGTH]; } + +// TODO: consider moving the defintion+impl inside `issue_14681` +// once https://github.com/rust-lang/rust/issues/146786 is fixed +#[derive(Clone, Copy)] +struct S; + +impl S { + fn new() -> Self { + println!("This is a side effect"); + S + } +} + +// should not trigger on non-function calls +fn issue_14681() { + fn foo(_s: &[Option]) {} + + foo(&[Some(0i64); 0]); + foo(&[Some(Some(0i64)); 0]); + foo(&[Some(f()); 0]); + //~^ zero_repeat_side_effects + foo(&[Some(Some(S::new())); 0]); + //~^ zero_repeat_side_effects +} diff --git a/tests/ui/zero_repeat_side_effects.stderr b/tests/ui/zero_repeat_side_effects.stderr index 2dba52e2112ec..771b71c686ae4 100644 --- a/tests/ui/zero_repeat_side_effects.stderr +++ b/tests/ui/zero_repeat_side_effects.stderr @@ -1,59 +1,136 @@ -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 +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:16: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)]` +help: consider performing the side effect separately + | +LL - let a = [f(); 0]; +LL + 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 +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:19:5 | LL | b = [f(); 0]; - | ^^^^^^^^^^^^ help: consider using: `f(); b = [] as [i32; 0]` + | ^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - b = [f(); 0]; +LL + 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 +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:24:5 | LL | let c = vec![f(); 0]; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f(); let c: std::vec::Vec = vec![];` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - let c = vec![f(); 0]; +LL + 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:29:5 +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:27:5 | LL | d = vec![f(); 0]; - | ^^^^^^^^^^^^^^^^ help: consider using: `f(); d = vec![] as std::vec::Vec` + | ^^^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - d = vec![f(); 0]; +LL + 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:33:5 +error: expression with side effects as the initial value in a zero-sized array initializer + --> 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] = [];` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - let e = [println!("side effect"); 0]; +LL + 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:37:5 +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:35:5 | LL | let g = [{ f() }; 0]; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `{ f() }; let g: [i32; 0] = [];` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - let g = [{ f() }; 0]; +LL + { 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:41:10 +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:39:10 | LL | drop(vec![f(); 0]); - | ^^^^^^^^^^^^ help: consider using: `{ f(); vec![] as std::vec::Vec }` + | ^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - drop(vec![f(); 0]); +LL + drop({ 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:45:5 +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:43:5 | LL | vec![f(); 0]; - | ^^^^^^^^^^^^ help: consider using: `{ f(); vec![] as std::vec::Vec }` + | ^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - vec![f(); 0]; +LL + { 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:47:5 +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:45:5 | LL | [f(); 0]; - | ^^^^^^^^ help: consider using: `{ f(); [] as [i32; 0] }` + | ^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - [f(); 0]; +LL + { f(); [] as [i32; 0] }; + | + +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:99:10 + | +LL | foo(&[Some(f()); 0]); + | ^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - foo(&[Some(f()); 0]); +LL + foo(&{ Some(f()); [] as [std::option::Option; 0] }); + | + +error: expression with side effects as the initial value in a zero-sized array initializer + --> tests/ui/zero_repeat_side_effects.rs:101:10 + | +LL | foo(&[Some(Some(S::new())); 0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider performing the side effect separately + | +LL - foo(&[Some(Some(S::new())); 0]); +LL + foo(&{ Some(Some(S::new())); [] as [std::option::Option>; 0] }); + | -error: aborting due to 9 previous errors +error: aborting due to 11 previous errors diff --git a/triagebot.toml b/triagebot.toml index 7b19f8658c088..b2fb50918f583 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -60,7 +60,6 @@ contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIB users_on_vacation = [ "matthiaskrgr", "Manishearth", - "flip1995", ] [assign.owners] From f97b493fa5338d98e161908d21a0e0bd90c643c9 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Sun, 5 Oct 2025 10:38:32 +0300 Subject: [PATCH 16/89] Remove no-rustfix --- tests/ui/char_lit_as_u8_unfixable.rs | 1 - tests/ui/char_lit_as_u8_unfixable.stderr | 2 +- tests/ui/must_use_unit_unfixable.rs | 2 -- tests/ui/must_use_unit_unfixable.stderr | 8 ++++---- tests/ui/needless_borrow_pat.fixed | 2 -- tests/ui/needless_borrow_pat.rs | 2 -- tests/ui/needless_borrow_pat.stderr | 24 ++++++++++++------------ 7 files changed, 17 insertions(+), 24 deletions(-) diff --git a/tests/ui/char_lit_as_u8_unfixable.rs b/tests/ui/char_lit_as_u8_unfixable.rs index e5c094f158ec7..c8774c7f30916 100644 --- a/tests/ui/char_lit_as_u8_unfixable.rs +++ b/tests/ui/char_lit_as_u8_unfixable.rs @@ -1,4 +1,3 @@ -//@no-rustfix #![warn(clippy::char_lit_as_u8)] fn main() { diff --git a/tests/ui/char_lit_as_u8_unfixable.stderr b/tests/ui/char_lit_as_u8_unfixable.stderr index 49e555ae638a2..4c2770cd2a4e0 100644 --- a/tests/ui/char_lit_as_u8_unfixable.stderr +++ b/tests/ui/char_lit_as_u8_unfixable.stderr @@ -1,5 +1,5 @@ error: casting a character literal to `u8` truncates - --> tests/ui/char_lit_as_u8_unfixable.rs:6:13 + --> tests/ui/char_lit_as_u8_unfixable.rs:5:13 | LL | let _ = '❤' as u8; | ^^^^^^^^^ diff --git a/tests/ui/must_use_unit_unfixable.rs b/tests/ui/must_use_unit_unfixable.rs index 0dba7996bac33..8eeaf36dca290 100644 --- a/tests/ui/must_use_unit_unfixable.rs +++ b/tests/ui/must_use_unit_unfixable.rs @@ -1,5 +1,3 @@ -//@no-rustfix - #[cfg_attr(all(), must_use, deprecated)] fn issue_12320() {} //~^ must_use_unit diff --git a/tests/ui/must_use_unit_unfixable.stderr b/tests/ui/must_use_unit_unfixable.stderr index 087682199afb9..8b5e556b1b2ed 100644 --- a/tests/ui/must_use_unit_unfixable.stderr +++ b/tests/ui/must_use_unit_unfixable.stderr @@ -1,11 +1,11 @@ error: this unit-returning function has a `#[must_use]` attribute - --> tests/ui/must_use_unit_unfixable.rs:4:1 + --> tests/ui/must_use_unit_unfixable.rs:2:1 | LL | fn issue_12320() {} | ^^^^^^^^^^^^^^^^ | help: remove `must_use` - --> tests/ui/must_use_unit_unfixable.rs:3:19 + --> tests/ui/must_use_unit_unfixable.rs:1:19 | LL | #[cfg_attr(all(), must_use, deprecated)] | ^^^^^^^^ @@ -13,13 +13,13 @@ LL | #[cfg_attr(all(), must_use, deprecated)] = help: to override `-D warnings` add `#[allow(clippy::must_use_unit)]` error: this unit-returning function has a `#[must_use]` attribute - --> tests/ui/must_use_unit_unfixable.rs:8:1 + --> tests/ui/must_use_unit_unfixable.rs:6:1 | LL | fn issue_12320_2() {} | ^^^^^^^^^^^^^^^^^^ | help: remove `must_use` - --> tests/ui/must_use_unit_unfixable.rs:7:44 + --> tests/ui/must_use_unit_unfixable.rs:5:44 | LL | #[cfg_attr(all(), deprecated, doc = "foo", must_use)] | ^^^^^^^^ diff --git a/tests/ui/needless_borrow_pat.fixed b/tests/ui/needless_borrow_pat.fixed index fe966a716df71..507186676c160 100644 --- a/tests/ui/needless_borrow_pat.fixed +++ b/tests/ui/needless_borrow_pat.fixed @@ -1,5 +1,3 @@ -// FIXME: run-rustfix waiting on multi-span suggestions - #![warn(clippy::needless_borrow)] #![allow(clippy::needless_borrowed_reference, clippy::explicit_auto_deref)] diff --git a/tests/ui/needless_borrow_pat.rs b/tests/ui/needless_borrow_pat.rs index a6b43855cad10..ef0f97301bcf2 100644 --- a/tests/ui/needless_borrow_pat.rs +++ b/tests/ui/needless_borrow_pat.rs @@ -1,5 +1,3 @@ -// FIXME: run-rustfix waiting on multi-span suggestions - #![warn(clippy::needless_borrow)] #![allow(clippy::needless_borrowed_reference, clippy::explicit_auto_deref)] diff --git a/tests/ui/needless_borrow_pat.stderr b/tests/ui/needless_borrow_pat.stderr index 25c570eb7ff73..34f167cca2235 100644 --- a/tests/ui/needless_borrow_pat.stderr +++ b/tests/ui/needless_borrow_pat.stderr @@ -1,5 +1,5 @@ error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:59:14 + --> tests/ui/needless_borrow_pat.rs:57:14 | LL | Some(ref x) => x, | ^^^^^ help: try: `x` @@ -8,7 +8,7 @@ LL | Some(ref x) => x, = help: to override `-D warnings` add `#[allow(clippy::needless_borrow)]` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:66:14 + --> tests/ui/needless_borrow_pat.rs:64:14 | LL | Some(ref x) => *x, | ^^^^^ @@ -20,7 +20,7 @@ LL + Some(x) => x, | error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:73:14 + --> tests/ui/needless_borrow_pat.rs:71:14 | LL | Some(ref x) => { | ^^^^^ @@ -35,19 +35,19 @@ LL ~ f1(x); | error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:85:14 + --> tests/ui/needless_borrow_pat.rs:83:14 | LL | Some(ref x) => m1!(x), | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:91:15 + --> tests/ui/needless_borrow_pat.rs:89:15 | LL | let _ = |&ref x: &&String| { | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:98:10 + --> tests/ui/needless_borrow_pat.rs:96:10 | LL | let (ref y,) = (&x,); | ^^^^^ @@ -61,13 +61,13 @@ LL ~ let _: &String = y; | error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:110:14 + --> tests/ui/needless_borrow_pat.rs:108:14 | LL | Some(ref x) => x.0, | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:121:14 + --> tests/ui/needless_borrow_pat.rs:119:14 | LL | E::A(ref x) | E::B(ref x) => *x, | ^^^^^ ^^^^^ @@ -79,13 +79,13 @@ LL + E::A(x) | E::B(x) => x, | error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:128:21 + --> tests/ui/needless_borrow_pat.rs:126:21 | LL | if let Some(ref x) = Some(&String::new()); | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:138:12 + --> tests/ui/needless_borrow_pat.rs:136:12 | LL | fn f2<'a>(&ref x: &&'a String) -> &'a String { | ^^^^^ @@ -100,13 +100,13 @@ LL ~ x | error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:147:11 + --> tests/ui/needless_borrow_pat.rs:145:11 | LL | fn f(&ref x: &&String) { | ^^^^^ help: try: `x` error: this pattern creates a reference to a reference - --> tests/ui/needless_borrow_pat.rs:157:11 + --> tests/ui/needless_borrow_pat.rs:155:11 | LL | fn f(&ref x: &&String) { | ^^^^^ From 5318883d754567c851d40aec8a5c0e875a7b0aae Mon Sep 17 00:00:00 2001 From: Nick Drozd Date: Sat, 6 Sep 2025 19:17:10 -0400 Subject: [PATCH 17/89] Use expect for lint warnings --- clippy_config/src/conf.rs | 4 ++-- clippy_config/src/types.rs | 2 +- clippy_dev/src/dogfood.rs | 2 +- clippy_dev/src/main.rs | 2 +- clippy_dev/src/new_lint.rs | 1 - .../src/arbitrary_source_item_ordering.rs | 2 +- clippy_lints/src/deprecated_lints.rs | 1 - clippy_lints/src/doc/mod.rs | 2 +- clippy_lints/src/eta_reduction.rs | 2 +- clippy_lints/src/format.rs | 1 - clippy_lints/src/format_args.rs | 2 +- clippy_lints/src/functions/must_use.rs | 2 +- clippy_lints/src/functions/ref_option.rs | 2 +- clippy_lints/src/implicit_saturating_sub.rs | 6 +++--- clippy_lints/src/lifetimes.rs | 4 ++-- clippy_lints/src/loops/mod.rs | 2 +- clippy_lints/src/loops/never_loop.rs | 2 +- clippy_lints/src/matches/collapsible_match.rs | 2 +- clippy_lints/src/methods/clone_on_copy.rs | 1 - clippy_lints/src/methods/filter_map.rs | 2 +- .../src/methods/map_all_any_identity.rs | 2 +- clippy_lints/src/methods/mod.rs | 3 +-- clippy_lints/src/methods/search_is_some.rs | 2 +- clippy_lints/src/methods/str_splitn.rs | 1 - .../src/methods/unnecessary_to_owned.rs | 2 +- .../src/methods/wrong_self_convention.rs | 1 - clippy_lints/src/needless_maybe_sized.rs | 2 +- clippy_lints/src/needless_pass_by_ref_mut.rs | 2 -- clippy_lints/src/only_used_in_recursion.rs | 2 +- clippy_lints/src/operators/bit_mask.rs | 1 - clippy_lints/src/swap.rs | 2 +- clippy_lints/src/trait_bounds.rs | 2 +- clippy_lints/src/types/mod.rs | 2 +- .../src/undocumented_unsafe_blocks.rs | 2 -- clippy_lints/src/utils/author.rs | 3 +-- clippy_utils/src/ast_utils/mod.rs | 4 ++-- clippy_utils/src/check_proc_macro.rs | 19 ++++++++----------- clippy_utils/src/consts.rs | 2 +- clippy_utils/src/higher.rs | 2 +- clippy_utils/src/lib.rs | 7 ++++--- clippy_utils/src/mir/mod.rs | 2 +- clippy_utils/src/mir/possible_borrower.rs | 2 -- clippy_utils/src/mir/possible_origin.rs | 1 - clippy_utils/src/source.rs | 2 +- clippy_utils/src/ty/mod.rs | 1 - clippy_utils/src/ty/type_certainty/mod.rs | 2 +- clippy_utils/src/visitors.rs | 2 +- lintcheck/src/config.rs | 2 +- lintcheck/src/input.rs | 2 +- lintcheck/src/main.rs | 4 ++-- src/driver.rs | 7 ++----- src/main.rs | 2 -- tests/compile-test.rs | 3 +-- tests/symbols-used.rs | 1 - 54 files changed, 57 insertions(+), 83 deletions(-) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 9ad434604dfcf..843aa6a40f098 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -248,7 +248,7 @@ macro_rules! define_Conf { #[derive(Deserialize)] #[serde(field_identifier, rename_all = "kebab-case")] - #[allow(non_camel_case_types)] + #[expect(non_camel_case_types)] enum Field { $($name,)* third_party, } struct ConfVisitor<'a>(&'a SourceFile); @@ -1213,7 +1213,7 @@ mod tests { for entry in toml_files { let file = fs::read_to_string(entry.path()).unwrap(); - #[allow(clippy::zero_sized_map_values)] + #[expect(clippy::zero_sized_map_values)] if let Ok(map) = toml::from_str::>(&file) { for name in map.keys() { names.remove(name.as_str()); diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index f64eefa0c232d..0dd65dfcfd6e0 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -131,7 +131,7 @@ impl DisallowedPathEnum { } /// Creates a map of disallowed items to the reason they were disallowed. -#[allow(clippy::type_complexity)] +#[expect(clippy::type_complexity)] pub fn create_disallowed_map( tcx: TyCtxt<'_>, disallowed_paths: &'static [DisallowedPath], diff --git a/clippy_dev/src/dogfood.rs b/clippy_dev/src/dogfood.rs index d0fca952b9322..9eb323eaef5aa 100644 --- a/clippy_dev/src/dogfood.rs +++ b/clippy_dev/src/dogfood.rs @@ -4,7 +4,7 @@ use itertools::Itertools; /// # Panics /// /// Panics if unable to run the dogfood test -#[allow(clippy::fn_params_excessive_bools)] +#[expect(clippy::fn_params_excessive_bools)] pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool, allow_no_vcs: bool) { run_exit_on_err( "cargo test", diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 5fef231f6ca1c..1b6a590b896f4 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -192,7 +192,7 @@ enum DevCommand { /// Which lint's page to load initially (optional) lint: Option, }, - #[allow(clippy::doc_markdown)] + #[expect(clippy::doc_markdown)] /// Manually run clippy on a file or package /// /// ## Examples diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 4121daa85e6d7..a14afd8c5f416 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -443,7 +443,6 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R Ok(()) } -#[allow(clippy::too_many_lines)] fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> { let lint_name_upper = lint.name.to_uppercase(); diff --git a/clippy_lints/src/arbitrary_source_item_ordering.rs b/clippy_lints/src/arbitrary_source_item_ordering.rs index 36498adff502e..b8bf8b25b3233 100644 --- a/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -167,7 +167,7 @@ declare_clippy_lint! { impl_lint_pass!(ArbitrarySourceItemOrdering => [ARBITRARY_SOURCE_ITEM_ORDERING]); #[derive(Debug)] -#[allow(clippy::struct_excessive_bools)] // Bools are cached feature flags. +#[expect(clippy::struct_excessive_bools, reason = "Bools are cached feature flags")] pub struct ArbitrarySourceItemOrdering { assoc_types_order: SourceItemOrderingTraitAssocItemKinds, enable_ordering_for_enum: bool, diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 2147f72889093..f087a894d76ae 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -7,7 +7,6 @@ macro_rules! declare_with_version { $e:expr, )*]) => { pub static $name: &[(&str, &str)] = &[$($e),*]; - #[allow(unused)] pub static $name_version: &[&str] = &[$($version),*]; }; } diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index f8ae770b3a4db..8fe3bc9e379fe 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -968,7 +968,7 @@ fn check_for_code_clusters<'a, Events: Iterator, Range)>>( cx: &LateContext<'_>, valid_idents: &FxHashSet, diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 752f39b4e6dc1..7c083eab88942 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -86,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { } } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx>>, expr: &Expr<'tcx>) { let body = if let ExprKind::Closure(c) = expr.kind && c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 94e66769eb265..098bf4ba42f93 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -39,7 +39,6 @@ declare_clippy_lint! { "useless use of `format!`" } -#[allow(clippy::module_name_repetitions)] pub struct UselessFormat { format_args: FormatArgsStorage, } diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 3359aa603239e..35965f4977cfc 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -237,7 +237,7 @@ impl_lint_pass!(FormatArgs<'_> => [ POINTER_FORMAT, ]); -#[allow(clippy::struct_field_names)] +#[expect(clippy::struct_field_names)] pub struct FormatArgs<'tcx> { format_args: FormatArgsStorage, msrv: Msrv, diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 8de68bfcb511b..68532de0368f8 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -132,7 +132,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr } // FIXME: needs to be an EARLY LINT. all attribute lints should be -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_needless_must_use( cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, diff --git a/clippy_lints/src/functions/ref_option.rs b/clippy_lints/src/functions/ref_option.rs index 5dc1b7269b761..cc9dc47e15f2b 100644 --- a/clippy_lints/src/functions/ref_option.rs +++ b/clippy_lints/src/functions/ref_option.rs @@ -62,7 +62,7 @@ fn check_fn_sig<'a>(cx: &LateContext<'a>, decl: &FnDecl<'a>, span: Span, sig: ty } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] pub(crate) fn check_fn<'a>( cx: &LateContext<'a>, kind: FnKind<'a>, diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index c634c12e1877b..678a29924e528 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -112,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_manual_check<'tcx>( cx: &LateContext<'tcx>, expr: &Expr<'tcx>, @@ -165,7 +165,7 @@ fn check_manual_check<'tcx>( } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_gt( cx: &LateContext<'_>, condition_span: Span, @@ -196,7 +196,7 @@ fn is_side_effect_free(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { eq_expr_value(cx, expr, expr) } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_subtraction( cx: &LateContext<'_>, condition_span: Span, diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index d8b186b6787d1..519ec228c8840 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -184,7 +184,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_fn_inner<'tcx>( cx: &LateContext<'tcx>, sig: &'tcx FnSig<'_>, @@ -540,7 +540,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ false } -#[allow(clippy::struct_excessive_bools)] +#[expect(clippy::struct_excessive_bools)] struct Usage { lifetime: Lifetime, in_where_predicate: bool, diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 7d14aa87d820a..b5d6da478d8a3 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -881,7 +881,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { } impl Loops { - #[allow(clippy::too_many_arguments)] + #[expect(clippy::too_many_arguments)] fn check_for_loop<'tcx>( &self, cx: &LateContext<'tcx>, diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 544c3c34d029c..528cc64fa7bc5 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -240,7 +240,7 @@ fn is_label_for_block(cx: &LateContext<'_>, dest: &Destination) -> bool { .is_ok_and(|hir_id| matches!(cx.tcx.hir_node(hir_id), Node::Block(_))) } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn never_loop_expr<'tcx>( cx: &LateContext<'tcx>, expr: &Expr<'tcx>, diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index aaf559fc4439e..ae3d35b1200c9 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -35,7 +35,7 @@ pub(super) fn check_if_let<'tcx>( check_arm(cx, false, pat, let_expr, body, None, else_expr, msrv); } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn check_arm<'tcx>( cx: &LateContext<'tcx>, outer_is_match: bool, diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index 0a456d1057ad9..52ae5b7d01b3a 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -12,7 +12,6 @@ use rustc_span::symbol::{Symbol, sym}; use super::CLONE_ON_COPY; /// Checks for the `CLONE_ON_COPY` lint. -#[allow(clippy::too_many_lines)] pub(super) fn check( cx: &LateContext<'_>, expr: &Expr<'_>, diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 2da0f8341b17b..dc742fa058cb6 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -280,7 +280,7 @@ fn is_filter_ok_map_unwrap(cx: &LateContext<'_>, expr: &Expr<'_>, filter_arg: &E } /// lint use of `filter().map()` or `find().map()` for `Iterators` -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] pub(super) fn check( cx: &LateContext<'_>, expr: &Expr<'_>, diff --git a/clippy_lints/src/methods/map_all_any_identity.rs b/clippy_lints/src/methods/map_all_any_identity.rs index ac11baa2d54ca..92b273f557186 100644 --- a/clippy_lints/src/methods/map_all_any_identity.rs +++ b/clippy_lints/src/methods/map_all_any_identity.rs @@ -8,7 +8,7 @@ use rustc_span::{Span, sym}; use super::MAP_ALL_ANY_IDENTITY; -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] pub(super) fn check( cx: &LateContext<'_>, expr: &Expr<'_>, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b0b4e5eedb084..a8f1ec7e91986 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4886,7 +4886,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } - #[allow(clippy::too_many_lines)] fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { if impl_item.span.in_external_macro(cx.sess().source_map()) { return; @@ -4958,7 +4957,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } impl Methods { - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Handle method calls whose receiver and arguments may not come from expansion if let Some((name, recv, args, span, call_span)) = method_call(expr) { diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 855babb797a21..1478bc29aef51 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -14,7 +14,7 @@ use super::SEARCH_IS_SOME; /// lint searching an Iterator followed by `is_some()` /// or calling `find()` on a string followed by `is_some()` or `is_none()` -#[allow(clippy::too_many_arguments, clippy::too_many_lines)] +#[expect(clippy::too_many_arguments, clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'_>, expr: &'tcx hir::Expr<'_>, diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 8daa5db887acf..a1a482deb2c30 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -271,7 +271,6 @@ struct IterUsage { span: Span, } -#[allow(clippy::too_many_lines)] fn parse_iter_usage<'tcx>( cx: &LateContext<'tcx>, ctxt: SyntaxContext, diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 640931a828998..6d927aef8b024 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -66,7 +66,7 @@ pub fn check<'tcx>( /// Checks whether `expr` is a referent in an `AddrOf` expression and, if so, determines whether its /// call of a `to_owned`-like function is unnecessary. -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn check_addr_of_expr( cx: &LateContext<'_>, expr: &Expr<'_>, diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index 74b297c13621e..12a6f345168fe 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -81,7 +81,6 @@ impl fmt::Display for Convention { } } -#[allow(clippy::too_many_arguments)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, item_name: Symbol, diff --git a/clippy_lints/src/needless_maybe_sized.rs b/clippy_lints/src/needless_maybe_sized.rs index ad6313e391bd9..4bcd26c74f57f 100644 --- a/clippy_lints/src/needless_maybe_sized.rs +++ b/clippy_lints/src/needless_maybe_sized.rs @@ -33,7 +33,7 @@ declare_clippy_lint! { } declare_lint_pass!(NeedlessMaybeSized => [NEEDLESS_MAYBE_SIZED]); -#[allow(clippy::struct_field_names)] +#[expect(clippy::struct_field_names)] struct Bound<'tcx> { /// The [`DefId`] of the type parameter the bound refers to param: DefId, diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 7052e1d0fbe5d..3d2285efbe185 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -364,7 +364,6 @@ impl MutablyUsedVariablesCtxt<'_> { } impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { - #[allow(clippy::if_same_then_else)] fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId) { if let euv::Place { base: @@ -398,7 +397,6 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { fn use_cloned(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {} - #[allow(clippy::if_same_then_else)] fn borrow(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId, borrow: ty::BorrowKind) { self.prev_bind = None; if let euv::Place { diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index ec8c2299d8cbb..b9cdae0f267ee 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -212,7 +212,7 @@ impl Usage { /// The parameters being checked by the lint, indexed by both the parameter's `HirId` and the /// `DefId` of the function paired with the parameter's index. #[derive(Default)] -#[allow(clippy::struct_field_names)] +#[expect(clippy::struct_field_names)] struct Params { params: Vec, by_id: HirIdMap, diff --git a/clippy_lints/src/operators/bit_mask.rs b/clippy_lints/src/operators/bit_mask.rs index e87cfd103c301..d6af0234f010b 100644 --- a/clippy_lints/src/operators/bit_mask.rs +++ b/clippy_lints/src/operators/bit_mask.rs @@ -47,7 +47,6 @@ fn check_compare<'a>(cx: &LateContext<'a>, bit_op: &Expr<'a>, cmp_op: BinOpKind, } } -#[allow(clippy::too_many_lines)] fn check_bit_mask( cx: &LateContext<'_>, bit_op: BinOpKind, diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 76ab3cdae22ef..f5400286f8845 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for Swap { } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn generate_swap_warning<'tcx>( block: &'tcx Block<'tcx>, cx: &LateContext<'tcx>, diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 9182a55081f40..352b8526b0217 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -238,7 +238,7 @@ impl TraitBounds { } } - #[allow(clippy::mutable_key_type)] + #[expect(clippy::mutable_key_type)] fn check_type_repetition<'tcx>(&self, cx: &LateContext<'tcx>, generics: &'tcx Generics<'_>) { struct SpanlessTy<'cx, 'tcx> { ty: &'tcx Ty<'tcx>, diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 515be5adeed0b..ccb027f77bf5f 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -693,7 +693,7 @@ impl Types { } } -#[allow(clippy::struct_excessive_bools, clippy::struct_field_names)] +#[expect(clippy::struct_excessive_bools)] #[derive(Clone, Copy, Default)] struct CheckTyContext { is_in_trait_impl: bool, diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index ba0d4de5f3b3b..b37f2a27f9051 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -489,7 +489,6 @@ enum HasSafetyComment { } /// Checks if the lines immediately preceding the item contain a safety comment. -#[allow(clippy::collapsible_match)] fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSafetyComment { match span_from_macro_expansion_has_safety_comment(cx, item.span) { HasSafetyComment::Maybe => (), @@ -551,7 +550,6 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf } /// Checks if the lines immediately preceding the item contain a safety comment. -#[allow(clippy::collapsible_match)] fn stmt_has_safety_comment( cx: &LateContext<'_>, span: Span, diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index ece29362a39f9..08210ae2cefb9 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -205,7 +205,6 @@ struct PrintVisitor<'a, 'tcx> { first: Cell, } -#[allow(clippy::unused_self)] impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>) -> Self { Self { @@ -410,7 +409,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.expr(field!(arm.body)); } - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn expr(&self, expr: &Binding<&hir::Expr<'_>>) { if let Some(higher::While { condition, body, .. }) = higher::While::hir(expr.value) { bind!(self, condition, body); diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index ad69e6eb184e1..b01e160e22972 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -143,7 +143,7 @@ pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool { } } -#[allow(clippy::too_many_lines)] // Just a big match statement +#[expect(clippy::too_many_lines, reason = "big match statement")] pub fn eq_expr(l: &Expr, r: &Expr) -> bool { use ExprKind::*; if !over(&l.attrs, &r.attrs, eq_attr) { @@ -328,7 +328,7 @@ pub fn eq_item(l: &Item, r: &Item, mut eq_kind: impl FnMut(&K, &K) -> b over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind) } -#[expect(clippy::too_many_lines)] // Just a big match statement +#[expect(clippy::too_many_lines, reason = "big match statement")] pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { use ItemKind::*; match (l, r) { diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 948a7203402d5..ff3e7b94f03bf 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -490,17 +490,14 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { None => ident_search_pat(last.ident).1, } } else { - // this shouldn't be possible, but sure - #[allow( - clippy::collapsible_else_if, - reason = "we want to keep these cases together, since they are both impossible" - )] - if qself_path.is_some() { - // last `>` in `` - Pat::Str(">") - } else { - Pat::Str("") - } + // this shouldn't be possible + Pat::Str( + if qself_path.is_some() { + ">" // last `>` in `` + } else { + "" + } + ) }; (start, end) }, diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 9ba796137cc3a..e03ba2f73b437 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -2,7 +2,7 @@ //! //! This cannot use rustc's const eval, aka miri, as arbitrary HIR expressions cannot be lowered to //! executable MIR bodies, so we have to do this instead. -#![allow(clippy::float_cmp)] +#![expect(clippy::float_cmp)] use crate::source::{SpanRangeExt, walk_span_to_context}; use crate::{clip, is_direct_expn_of, paths, sext, sym, unsext}; diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 6f1bc28fbab81..95e6ecd40dec6 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -212,7 +212,7 @@ pub struct Range<'a> { impl<'a> Range<'a> { /// Higher a `hir` range to something similar to `ast::ExprKind::Range`. - #[allow(clippy::similar_names)] + #[expect(clippy::similar_names)] pub fn hir(expr: &'a Expr<'_>) -> Option> { match expr.kind { ExprKind::Call(path, [arg1, arg2]) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 24864e8ef96dc..9ae3663057720 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -31,8 +31,10 @@ extern crate rustc_ast; extern crate rustc_attr_parsing; extern crate rustc_const_eval; extern crate rustc_data_structures; -// The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate. -#[allow(unused_extern_crates)] +#[expect( + unused_extern_crates, + reason = "The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate." +)] extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; @@ -2813,7 +2815,6 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'tcx>) -> ExprUseCtx moved_before_use, same_ctxt, }, - #[allow(unreachable_patterns)] Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow"), None => ExprUseCtxt { node: Node::Crate(cx.tcx.hir_root_module()), diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs index 9ba644fdd20ec..a066427d6bc11 100644 --- a/clippy_utils/src/mir/mod.rs +++ b/clippy_utils/src/mir/mod.rs @@ -134,7 +134,7 @@ pub fn used_exactly_once(mir: &Body<'_>, local: Local) -> Option { } /// Returns the `mir::Body` containing the node associated with `hir_id`. -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> Option<&Body<'_>> { let body_owner_local_def_id = tcx.hir_enclosing_body_owner(hir_id); if tcx.hir_body_owner_kind(body_owner_local_def_id).is_fn_or_closure() { diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs index 152b4272c26ca..f2bffc8156afc 100644 --- a/clippy_utils/src/mir/possible_borrower.rs +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -15,7 +15,6 @@ use std::ops::ControlFlow; /// Collects the possible borrowers of each local. /// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` /// possible borrowers of `a`. -#[allow(clippy::module_name_repetitions)] struct PossibleBorrowerVisitor<'a, 'b, 'tcx> { possible_borrower: TransitiveRelation, body: &'b mir::Body<'tcx>, @@ -167,7 +166,6 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { } /// Result of `PossibleBorrowerVisitor`. -#[allow(clippy::module_name_repetitions)] pub struct PossibleBorrowerMap<'b, 'tcx> { /// Mapping `Local -> its possible borrowers` pub map: FxHashMap>, diff --git a/clippy_utils/src/mir/possible_origin.rs b/clippy_utils/src/mir/possible_origin.rs index 3d253fd2bb148..fee22c436b0fb 100644 --- a/clippy_utils/src/mir/possible_origin.rs +++ b/clippy_utils/src/mir/possible_origin.rs @@ -8,7 +8,6 @@ use rustc_middle::mir; /// Collect possible borrowed for every `&mut` local. /// For example, `_1 = &mut _2` generate _1: {_2,...} /// Known Problems: not sure all borrowed are tracked -#[allow(clippy::module_name_repetitions)] pub(super) struct PossibleOriginVisitor<'a, 'tcx> { possible_origin: TransitiveRelation, body: &'a mir::Body<'tcx>, diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 638d329031231..8b1ec7e62e4dc 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -143,7 +143,7 @@ pub trait SpanRangeExt: SpanRange { map_range(cx.sess().source_map(), self.into_range(), f) } - #[allow(rustdoc::invalid_rust_codeblocks, reason = "The codeblock is intentionally broken")] + #[expect(rustdoc::invalid_rust_codeblocks, reason = "The codeblock is intentionally broken")] /// Extends the range to include all preceding whitespace characters. /// /// The range will not be expanded if it would cross a line boundary, the line the range would diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index ebf4f2cd3263c..c48b5b7c171ff 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -954,7 +954,6 @@ pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 { } /// Asserts that the given arguments match the generic parameters of the given item. -#[allow(dead_code)] fn assert_generic_args_match<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, args: &[GenericArg<'tcx>]) { let g = tcx.generics_of(did); let parent = g.parent.map(|did| tcx.generics_of(did)); diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index d9c7e6eac9f67..d46c7bdcd4c1d 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -329,7 +329,7 @@ fn update_res( None } -#[allow(clippy::cast_possible_truncation)] +#[expect(clippy::cast_possible_truncation)] fn type_is_inferable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let Some(callee_def_id) = (match expr.kind { ExprKind::Call(callee, _) => { diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index c9f5401ebe776..96f3c1fbe3e5a 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -591,7 +591,7 @@ pub fn for_each_local_use_after_expr<'tcx, B>( // Calls the given function for every unconsumed temporary created by the expression. Note the // function is only guaranteed to be called for types which need to be dropped, but it may be called // for other types. -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub fn for_each_unconsumed_temporary<'tcx, B>( cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>, diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index 3b2ebf0c28ac2..5d5214805b968 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -2,7 +2,7 @@ use clap::{Parser, Subcommand, ValueEnum}; use std::num::NonZero; use std::path::PathBuf; -#[allow(clippy::struct_excessive_bools)] +#[expect(clippy::struct_excessive_bools)] #[derive(Parser, Clone, Debug)] #[command(args_conflicts_with_subcommands = true)] pub(crate) struct LintcheckConfig { diff --git a/lintcheck/src/input.rs b/lintcheck/src/input.rs index 1ed059d2fb114..7dda2b7b25f81 100644 --- a/lintcheck/src/input.rs +++ b/lintcheck/src/input.rs @@ -180,7 +180,7 @@ impl CrateWithSource { /// copies a local folder #[expect(clippy::too_many_lines)] fn download_and_extract(&self) -> Crate { - #[allow(clippy::result_large_err)] + #[expect(clippy::result_large_err)] fn get(path: &str) -> Result { const MAX_RETRIES: u8 = 4; let mut retries = 0; diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 3a60cfa79f410..b30df79023784 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -66,7 +66,7 @@ struct Crate { impl Crate { /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy /// issued - #[allow(clippy::too_many_arguments, clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn run_clippy_lints( &self, clippy_driver_path: &Path, @@ -314,7 +314,7 @@ fn main() { } } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn lintcheck(config: LintcheckConfig) { let clippy_ver = build_clippy(config.perf); let clippy_driver_path = fs::canonicalize(format!( diff --git a/src/driver.rs b/src/driver.rs index 6bddcbfd94ce4..102ca3fa69f7e 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -133,8 +133,7 @@ struct ClippyCallbacks { } impl rustc_driver::Callbacks for ClippyCallbacks { - // JUSTIFICATION: necessary in clippy driver to set `mir_opt_level` - #[allow(rustc::bad_opt_access)] + #[expect(rustc::bad_opt_access, reason = "necessary in clippy driver to set `mir_opt_level`")] fn config(&mut self, config: &mut interface::Config) { let conf_path = clippy_config::lookup_conf_file(); let previous = config.register_lints.take(); @@ -182,15 +181,13 @@ impl rustc_driver::Callbacks for ClippyCallbacks { } } -#[allow(clippy::ignored_unit_patterns)] fn display_help() { println!("{}", help_message()); } const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml"; -#[allow(clippy::too_many_lines)] -#[allow(clippy::ignored_unit_patterns)] +#[expect(clippy::too_many_lines)] pub fn main() { // See docs in https://github.com/rust-lang/rust/blob/master/compiler/rustc/src/main.rs // about jemalloc. diff --git a/src/main.rs b/src/main.rs index 3c2eec1f05b90..688161c7bfcb9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,12 +10,10 @@ use std::process::{self, Command}; use anstream::println; -#[allow(clippy::ignored_unit_patterns)] fn show_help() { println!("{}", help_message()); } -#[allow(clippy::ignored_unit_patterns)] fn show_version() { let version_info = rustc_tools_util::get_version_info!(); println!("{version_info}"); diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 71cd8a6c03cc9..1ac6889352786 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -291,7 +291,6 @@ fn run_ui_toml(cx: &TestContext) { } // Allow `Default::default` as `OptWithSpan` is not nameable -#[allow(clippy::default_trait_access)] fn run_ui_cargo(cx: &TestContext) { if IS_RUSTC_TEST_SUITE { return; @@ -473,7 +472,7 @@ struct DiagnosticCollector { } impl DiagnosticCollector { - #[allow(clippy::assertions_on_constants)] + #[expect(clippy::assertions_on_constants)] fn spawn() -> (Self, thread::JoinHandle<()>) { assert!(!IS_RUSTC_TEST_SUITE && !RUN_INTERNAL_TESTS); diff --git a/tests/symbols-used.rs b/tests/symbols-used.rs index a1049ba64d549..f78f15103cc4e 100644 --- a/tests/symbols-used.rs +++ b/tests/symbols-used.rs @@ -18,7 +18,6 @@ type Result = std::result::Result; type AnyError = Box; #[test] -#[allow(clippy::case_sensitive_file_extension_comparisons)] fn all_symbols_are_used() -> Result<()> { if option_env!("RUSTC_TEST_SUITE").is_some() { return Ok(()); From 517ef604af21c2225ff32335f964cc806d6be8b5 Mon Sep 17 00:00:00 2001 From: +merlan #flirora Date: Tue, 7 Oct 2025 11:18:03 -0400 Subject: [PATCH 18/89] Add `replace_box` lint --- CHANGELOG.md | 373 ++++++++++++++++++++--------- clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/replace_box.rs | 130 ++++++++++ tests/ui/replace_box.fixed | 72 ++++++ tests/ui/replace_box.rs | 72 ++++++ tests/ui/replace_box.stderr | 52 ++++ 7 files changed, 595 insertions(+), 107 deletions(-) create mode 100644 clippy_lints/src/replace_box.rs create mode 100644 tests/ui/replace_box.fixed create mode 100644 tests/ui/replace_box.rs create mode 100644 tests/ui/replace_box.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index b6b374e26c962..89e3bd336c5a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,19 +28,19 @@ Note: This Clippy release does not introduce many new lints and is focused entir ### Enhancements -* [`or_fun_call`] now lints `Option::get_or_insert`, `Result::map_or`, `Option/Result::and` methods - [#15071](https://github.com/rust-lang/rust-clippy/pull/15071) - [#15073](https://github.com/rust-lang/rust-clippy/pull/15073) +* [`or_fun_call`] now lints `Option::get_or_insert`, `Result::map_or`, `Option/Result::and` methods + [#15071](https://github.com/rust-lang/rust-clippy/pull/15071) + [#15073](https://github.com/rust-lang/rust-clippy/pull/15073) [#15074](https://github.com/rust-lang/rust-clippy/pull/15074) -* [`incompatible_msrv`] now recognizes types exceeding MSRV +* [`incompatible_msrv`] now recognizes types exceeding MSRV [#15296](https://github.com/rust-lang/rust-clippy/pull/15296) -* [`incompatible_msrv`] now checks the right MSRV when in a `const` context +* [`incompatible_msrv`] now checks the right MSRV when in a `const` context [#15297](https://github.com/rust-lang/rust-clippy/pull/15297) -* [`zero_ptr`] now lints in `const` context as well +* [`zero_ptr`] now lints in `const` context as well [#15152](https://github.com/rust-lang/rust-clippy/pull/15152) * [`map_identity`],[`flat_map_identity`] now recognizes `|[x, y]| [x, y]` as an identity function [#15229](https://github.com/rust-lang/rust-clippy/pull/15229) -* [`exit`] no longer fails on the main function when using `--test` or `--all-targets` flag +* [`exit`] no longer fails on the main function when using `--test` or `--all-targets` flag [#15222](https://github.com/rust-lang/rust-clippy/pull/15222) ### False Positive Fixes @@ -51,7 +51,7 @@ Note: This Clippy release does not introduce many new lints and is focused entir [#15319](https://github.com/rust-lang/rust-clippy/pull/15319) * [`unused_async`] fixed FP on function with `todo!` [#15308](https://github.com/rust-lang/rust-clippy/pull/15308) -* [`useless_attribute`] fixed FP when using `#[expect(redundant_imports)]` and similar lint attributes +* [`useless_attribute`] fixed FP when using `#[expect(redundant_imports)]` and similar lint attributes on `use` statements [#15318](https://github.com/rust-lang/rust-clippy/pull/15318) * [`pattern_type_mismatch`] fixed FP in external macro @@ -64,7 +64,7 @@ Note: This Clippy release does not introduce many new lints and is focused entir [#15314](https://github.com/rust-lang/rust-clippy/pull/15314) * [`ptr_as_ptr`] fixed wrong suggestions with turbo fish [#15289](https://github.com/rust-lang/rust-clippy/pull/15289) -* [`range_plus_one`], [`range_minus_one`] fixed FP by restricting lint to cases where it is safe +* [`range_plus_one`], [`range_minus_one`] fixed FP by restricting lint to cases where it is safe to switch the range type [#14432](https://github.com/rust-lang/rust-clippy/pull/14432) * [`ptr_arg`] fixed FP with underscore binding to `&T` or `&mut T` argument @@ -129,7 +129,7 @@ Note: This Clippy release does not introduce many new lints and is focused entir [#15022](https://github.com/rust-lang/rust-clippy/pull/15022) * [`collapsible_else_if`] fixed FP on conditionally compiled stmt [#14906](https://github.com/rust-lang/rust-clippy/pull/14906) -* [`missing_const_for_fn`] fixed FP by checking MSRV before emitting lint on function containing +* [`missing_const_for_fn`] fixed FP by checking MSRV before emitting lint on function containing non-`Sized` trait bounds [#15080](https://github.com/rust-lang/rust-clippy/pull/15080) * [`question_mark`] fixed FP when else branch of let-else contains `#[cfg]` @@ -137,7 +137,7 @@ Note: This Clippy release does not introduce many new lints and is focused entir ### ICE Fixes -* [`single_match`] fixed ICE with deref patterns and string literals +* [`single_match`] fixed ICE with deref patterns and string literals [#15124](https://github.com/rust-lang/rust-clippy/pull/15124) * [`needless_doctest_main`] fixed panic when doctest is invalid [#15052](https://github.com/rust-lang/rust-clippy/pull/15052) @@ -146,11 +146,11 @@ Note: This Clippy release does not introduce many new lints and is focused entir ### Documentation Improvements -* [`manual_is_variant_and`] improved documentation to include equality comparison patterns +* [`manual_is_variant_and`] improved documentation to include equality comparison patterns [#15239](https://github.com/rust-lang/rust-clippy/pull/15239) * [`uninlined_format_args`] improved documentation with example of how to fix a `{:?}` parameter [#15228](https://github.com/rust-lang/rust-clippy/pull/15228) -* [`undocumented_unsafe_blocks`] improved documentation wording +* [`undocumented_unsafe_blocks`] improved documentation wording [#15213](https://github.com/rust-lang/rust-clippy/pull/15213) ## Rust 1.89 @@ -292,7 +292,7 @@ Current stable, released 2025-06-26 [#14408](https://github.com/rust-lang/rust-clippy/pull/14408) * [`iter_kv_map`] now recognizes references on maps [#14596](https://github.com/rust-lang/rust-clippy/pull/14596) -* [`empty_enum_variants_with_brackets`] no longer lints reachable enums or enums used +* [`empty_enum_variants_with_brackets`] no longer lints reachable enums or enums used as functions within same crate [#12971](https://github.com/rust-lang/rust-clippy/pull/12971) * [`needless_lifetimes`] now checks for lifetime uses in closures [#14608](https://github.com/rust-lang/rust-clippy/pull/14608) @@ -928,50 +928,50 @@ Released 2024-02-08 ### New Lints -- [`infinite_loop`] +* [`infinite_loop`] [#11829](https://github.com/rust-lang/rust-clippy/pull/11829) -- [`ineffective_open_options`] +* [`ineffective_open_options`] [#11902](https://github.com/rust-lang/rust-clippy/pull/11902) -- [`uninhabited_references`] +* [`uninhabited_references`] [#11878](https://github.com/rust-lang/rust-clippy/pull/11878) -- [`repeat_vec_with_capacity`] +* [`repeat_vec_with_capacity`] [#11597](https://github.com/rust-lang/rust-clippy/pull/11597) -- [`test_attr_in_doctest`] +* [`test_attr_in_doctest`] [#11872](https://github.com/rust-lang/rust-clippy/pull/11872) -- [`option_map_or_err_ok`] +* [`option_map_or_err_ok`] [#11864](https://github.com/rust-lang/rust-clippy/pull/11864) -- [`join_absolute_paths`] +* [`join_absolute_paths`] [#11453](https://github.com/rust-lang/rust-clippy/pull/11453) -- [`impl_hash_borrow_with_str_and_bytes`] +* [`impl_hash_borrow_with_str_and_bytes`] [#11781](https://github.com/rust-lang/rust-clippy/pull/11781) -- [`iter_over_hash_type`] +* [`iter_over_hash_type`] [#11791](https://github.com/rust-lang/rust-clippy/pull/11791) ### Moves and Deprecations -- Renamed `blocks_in_if_conditions` to [`blocks_in_conditions`] +* Renamed `blocks_in_if_conditions` to [`blocks_in_conditions`] [#11853](https://github.com/rust-lang/rust-clippy/pull/11853) -- Moved [`implied_bounds_in_impls`] to `complexity` (Now warn-by-default) +* Moved [`implied_bounds_in_impls`] to `complexity` (Now warn-by-default) [#11867](https://github.com/rust-lang/rust-clippy/pull/11867) -- Moved [`if_same_then_else`] to `style` (Now warn-by-default) +* Moved [`if_same_then_else`] to `style` (Now warn-by-default) [#11809](https://github.com/rust-lang/rust-clippy/pull/11809) ### Enhancements -- [`missing_safety_doc`], [`unnecessary_safety_doc`], [`missing_panics_doc`], [`missing_errors_doc`]: +* [`missing_safety_doc`], [`unnecessary_safety_doc`], [`missing_panics_doc`], [`missing_errors_doc`]: Added the [`check-private-items`] configuration to enable lints on private items [#11842](https://github.com/rust-lang/rust-clippy/pull/11842) ### ICE Fixes -- [`impl_trait_in_params`]: No longer crashes when a function has generics but no function parameters +* [`impl_trait_in_params`]: No longer crashes when a function has generics but no function parameters [#11804](https://github.com/rust-lang/rust-clippy/pull/11804) -- [`unused_enumerate_index`]: No longer crashes on empty tuples +* [`unused_enumerate_index`]: No longer crashes on empty tuples [#11756](https://github.com/rust-lang/rust-clippy/pull/11756) ### Others -- Clippy now respects the `CARGO` environment value +* Clippy now respects the `CARGO` environment value [#11944](https://github.com/rust-lang/rust-clippy/pull/11944) ## Rust 1.75 @@ -997,7 +997,6 @@ Released 2023-12-28 * [`manual_hash_one`] [#11556](https://github.com/rust-lang/rust-clippy/pull/11556) - ### Moves and Deprecations * Moved [`read_zero_byte_vec`] to `nursery` (Now allow-by-default) @@ -1073,7 +1072,7 @@ Released 2023-11-16 ### Enhancements -* [`undocumented_unsafe_blocks`]: The config values [`accept-comment-above-statement`] and +* [`undocumented_unsafe_blocks`]: The config values [`accept-comment-above-statement`] and [`accept-comment-above-attributes`] are now `true` by default [#11170](https://github.com/rust-lang/rust-clippy/pull/11170) * [`explicit_iter_loop`]: Added [`enforce-iter-loop-reborrow`] to disable reborrow linting by default @@ -2254,7 +2253,6 @@ Released 2022-09-22 * [`explicit_auto_deref`] [#8355](https://github.com/rust-lang/rust-clippy/pull/8355) - ### Moves and Deprecations * Moved [`format_push_string`] to `restriction` (now allow-by-default) @@ -2455,10 +2453,10 @@ Released 2022-08-11 * [`redundant_allocation`]: No longer lints on fat pointers that would become thin pointers [#8813](https://github.com/rust-lang/rust-clippy/pull/8813) * [`derive_partial_eq_without_eq`]: - * Handle differing predicates applied by `#[derive(PartialEq)]` and + * Handle differing predicates applied by `#[derive(PartialEq)]` and `#[derive(Eq)]` [#8869](https://github.com/rust-lang/rust-clippy/pull/8869) - * No longer lints on non-public types and better handles generics + * No longer lints on non-public types and better handles generics [#8950](https://github.com/rust-lang/rust-clippy/pull/8950) * [`empty_line_after_outer_attr`]: No longer lints empty lines in inner string values [#8892](https://github.com/rust-lang/rust-clippy/pull/8892) @@ -2952,12 +2950,12 @@ Released 2022-02-24 [#7957](https://github.com/rust-lang/rust-clippy/pull/7957) * [`needless_borrow`] [#7977](https://github.com/rust-lang/rust-clippy/pull/7977) - * Lint when a borrow is auto-dereffed more than once - * Lint in the trailing expression of a block for a match arm + * Lint when a borrow is auto-dereffed more than once + * Lint in the trailing expression of a block for a match arm * [`strlen_on_c_strings`] [8001](https://github.com/rust-lang/rust-clippy/pull/8001) - * Lint when used without a fully-qualified path - * Suggest removing the surrounding unsafe block when possible + * Lint when used without a fully-qualified path + * Suggest removing the surrounding unsafe block when possible * [`non_ascii_literal`]: Now also lints on `char`s, not just `string`s [#8034](https://github.com/rust-lang/rust-clippy/pull/8034) * [`single_char_pattern`]: Now also lints on `split_inclusive`, `split_once`, @@ -3068,7 +3066,7 @@ Released 2022-02-24 [#7813](https://github.com/rust-lang/rust-clippy/pull/7813) * New and improved issue templates [#8032](https://github.com/rust-lang/rust-clippy/pull/8032) -* _Dev:_ Add `cargo dev lint` command, to run your modified Clippy version on a +* *Dev:* Add `cargo dev lint` command, to run your modified Clippy version on a file [#7917](https://github.com/rust-lang/rust-clippy/pull/7917) ## Rust 1.58 @@ -3278,15 +3276,15 @@ Released 2021-12-02 [#7566](https://github.com/rust-lang/rust-clippy/pull/7566) * [`option_if_let_else`]: Multiple fixes [#7573](https://github.com/rust-lang/rust-clippy/pull/7573) - * `break` and `continue` statements local to the would-be closure are + * `break` and `continue` statements local to the would-be closure are allowed - * Don't lint in const contexts - * Don't lint when yield expressions are used - * Don't lint when the captures made by the would-be closure conflict with + * Don't lint in const contexts + * Don't lint when yield expressions are used + * Don't lint when the captures made by the would-be closure conflict with the other branch - * Don't lint when a field of a local is used when the type could be + * Don't lint when a field of a local is used when the type could be potentially moved from - * In some cases, don't lint when scrutinee expression conflicts with the + * In some cases, don't lint when scrutinee expression conflicts with the captures of the would-be closure * [`redundant_allocation`]: No longer lints on `Box>` which replaces wide pointers with thin pointers @@ -3535,124 +3533,124 @@ Released 2021-07-29 ### New Lints -- [`ref_binding_to_reference`] +* [`ref_binding_to_reference`] [#7105](https://github.com/rust-lang/rust-clippy/pull/7105) -- [`needless_bitwise_bool`] +* [`needless_bitwise_bool`] [#7133](https://github.com/rust-lang/rust-clippy/pull/7133) -- [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225) -- [`manual_str_repeat`] +* [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225) +* [`manual_str_repeat`] [#7265](https://github.com/rust-lang/rust-clippy/pull/7265) -- [`suspicious_splitn`] +* [`suspicious_splitn`] [#7292](https://github.com/rust-lang/rust-clippy/pull/7292) ### Moves and Deprecations -- Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of +* Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of the new `avoid-breaking-exported-api` config option (see [Enhancements](#1-54-enhancements)) [#7187](https://github.com/rust-lang/rust-clippy/pull/7187) -- Move [`inconsistent_struct_constructor`] to `pedantic` +* Move [`inconsistent_struct_constructor`] to `pedantic` [#7193](https://github.com/rust-lang/rust-clippy/pull/7193) -- Move [`needless_borrow`] to `style` (now warn-by-default) +* Move [`needless_borrow`] to `style` (now warn-by-default) [#7254](https://github.com/rust-lang/rust-clippy/pull/7254) -- Move [`suspicious_operation_groupings`] to `nursery` +* Move [`suspicious_operation_groupings`] to `nursery` [#7266](https://github.com/rust-lang/rust-clippy/pull/7266) -- Move [`semicolon_if_nothing_returned`] to `pedantic` +* Move [`semicolon_if_nothing_returned`] to `pedantic` [#7268](https://github.com/rust-lang/rust-clippy/pull/7268) ### Enhancements -- [`while_let_on_iterator`]: Now also lints in nested loops +* [`while_let_on_iterator`]: Now also lints in nested loops [#6966](https://github.com/rust-lang/rust-clippy/pull/6966) -- [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix` +* [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix` [#7156](https://github.com/rust-lang/rust-clippy/pull/7156) -- [`needless_collect`]: Now also lints on assignments with type annotations +* [`needless_collect`]: Now also lints on assignments with type annotations [#7163](https://github.com/rust-lang/rust-clippy/pull/7163) -- [`if_then_some_else_none`]: Now works with the MSRV config +* [`if_then_some_else_none`]: Now works with the MSRV config [#7177](https://github.com/rust-lang/rust-clippy/pull/7177) -- Add `avoid-breaking-exported-api` config option for the lints +* Add `avoid-breaking-exported-api` config option for the lints [`enum_variant_names`], [`large_types_passed_by_value`], [`trivially_copy_pass_by_ref`], [`unnecessary_wraps`], [`upper_case_acronyms`], and [`wrong_self_convention`]. We recommend to set this configuration option to `false` before a major release (1.0/2.0/...) to clean up the API [#7187](https://github.com/rust-lang/rust-clippy/pull/7187) -- [`needless_collect`]: Now lints on even more data structures +* [`needless_collect`]: Now lints on even more data structures [#7188](https://github.com/rust-lang/rust-clippy/pull/7188) -- [`missing_docs_in_private_items`]: No longer sees `#[ = ""]` like +* [`missing_docs_in_private_items`]: No longer sees `#[ = ""]` like attributes as sufficient documentation [#7281](https://github.com/rust-lang/rust-clippy/pull/7281) -- [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]: +* [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]: Now work as expected when used with `allow` [#7282](https://github.com/rust-lang/rust-clippy/pull/7282) ### False Positive Fixes -- [`implicit_return`]: Now takes all diverging functions in account to avoid +* [`implicit_return`]: Now takes all diverging functions in account to avoid false positives [#6951](https://github.com/rust-lang/rust-clippy/pull/6951) -- [`while_let_on_iterator`]: No longer lints when the iterator is a struct field +* [`while_let_on_iterator`]: No longer lints when the iterator is a struct field and the struct is used in the loop [#6966](https://github.com/rust-lang/rust-clippy/pull/6966) -- [`multiple_inherent_impl`]: No longer lints with generic arguments +* [`multiple_inherent_impl`]: No longer lints with generic arguments [#7089](https://github.com/rust-lang/rust-clippy/pull/7089) -- [`comparison_chain`]: No longer lints in a `const` context +* [`comparison_chain`]: No longer lints in a `const` context [#7118](https://github.com/rust-lang/rust-clippy/pull/7118) -- [`while_immutable_condition`]: Fix false positive where mutation in the loop +* [`while_immutable_condition`]: Fix false positive where mutation in the loop variable wasn't picked up [#7144](https://github.com/rust-lang/rust-clippy/pull/7144) -- [`default_trait_access`]: No longer lints in macros +* [`default_trait_access`]: No longer lints in macros [#7150](https://github.com/rust-lang/rust-clippy/pull/7150) -- [`needless_question_mark`]: No longer lints when the inner value is implicitly +* [`needless_question_mark`]: No longer lints when the inner value is implicitly dereferenced [#7165](https://github.com/rust-lang/rust-clippy/pull/7165) -- [`unused_unit`]: No longer lints when multiple macro contexts are involved +* [`unused_unit`]: No longer lints when multiple macro contexts are involved [#7167](https://github.com/rust-lang/rust-clippy/pull/7167) -- [`eval_order_dependence`]: Fix false positive in async context +* [`eval_order_dependence`]: Fix false positive in async context [#7174](https://github.com/rust-lang/rust-clippy/pull/7174) -- [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the +* [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the type [#7175](https://github.com/rust-lang/rust-clippy/pull/7175) -- [`wrong_self_convention`]: No longer lints in trait implementations of +* [`wrong_self_convention`]: No longer lints in trait implementations of non-`Copy` types [#7182](https://github.com/rust-lang/rust-clippy/pull/7182) -- [`suboptimal_flops`]: No longer lints on `powi(2)` +* [`suboptimal_flops`]: No longer lints on `powi(2)` [#7201](https://github.com/rust-lang/rust-clippy/pull/7201) -- [`wrong_self_convention`]: No longer lints if there is no implicit `self` +* [`wrong_self_convention`]: No longer lints if there is no implicit `self` [#7215](https://github.com/rust-lang/rust-clippy/pull/7215) -- [`option_if_let_else`]: No longer lints on `else if let` pattern +* [`option_if_let_else`]: No longer lints on `else if let` pattern [#7216](https://github.com/rust-lang/rust-clippy/pull/7216) -- [`use_self`], [`useless_conversion`]: Fix false positives when generic +* [`use_self`], [`useless_conversion`]: Fix false positives when generic arguments are involved [#7223](https://github.com/rust-lang/rust-clippy/pull/7223) -- [`manual_unwrap_or`]: Fix false positive with deref coercion +* [`manual_unwrap_or`]: Fix false positive with deref coercion [#7233](https://github.com/rust-lang/rust-clippy/pull/7233) -- [`similar_names`]: No longer lints on `wparam`/`lparam` +* [`similar_names`]: No longer lints on `wparam`/`lparam` [#7255](https://github.com/rust-lang/rust-clippy/pull/7255) -- [`redundant_closure`]: No longer lints on using the `vec![]` macro in a +* [`redundant_closure`]: No longer lints on using the `vec![]` macro in a closure [#7263](https://github.com/rust-lang/rust-clippy/pull/7263) ### Suggestion Fixes/Improvements -- [`implicit_return`] +* [`implicit_return`] [#6951](https://github.com/rust-lang/rust-clippy/pull/6951) - - Fix suggestion for async functions - - Improve suggestion with macros - - Suggest to change `break` to `return` when appropriate -- [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary + * Fix suggestion for async functions + * Improve suggestion with macros + * Suggest to change `break` to `return` when appropriate +* [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary [#6966](https://github.com/rust-lang/rust-clippy/pull/6966) -- [`match_single_binding`]: Improve suggestion when match scrutinee has side +* [`match_single_binding`]: Improve suggestion when match scrutinee has side effects [#7095](https://github.com/rust-lang/rust-clippy/pull/7095) -- [`needless_borrow`]: Now suggests to also change usage sites as needed +* [`needless_borrow`]: Now suggests to also change usage sites as needed [#7105](https://github.com/rust-lang/rust-clippy/pull/7105) -- [`write_with_newline`]: Improve suggestion when only `\n` is written to the +* [`write_with_newline`]: Improve suggestion when only `\n` is written to the buffer [#7183](https://github.com/rust-lang/rust-clippy/pull/7183) -- [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also +* [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also when a `<_ as Trait>::_` is involved [#7264](https://github.com/rust-lang/rust-clippy/pull/7264) -- [`not_unsafe_ptr_arg_deref`]: Improved error message +* [`not_unsafe_ptr_arg_deref`]: Improved error message [#7294](https://github.com/rust-lang/rust-clippy/pull/7294) ### ICE Fixes -- Fix ICE when running Clippy on `libstd` +* Fix ICE when running Clippy on `libstd` [#7140](https://github.com/rust-lang/rust-clippy/pull/7140) -- [`implicit_return`] +* [`implicit_return`] [#7242](https://github.com/rust-lang/rust-clippy/pull/7242) ## Rust 1.53 @@ -3697,9 +3695,9 @@ Released 2021-06-17 [#6828](https://github.com/rust-lang/rust-clippy/pull/6828) * [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]: [#6863](https://github.com/rust-lang/rust-clippy/pull/6863) - * Attempt to find a common path prefix in suggestion - * Don't lint on `Option` and `Result` - * Consider `Self` prefix + * Attempt to find a common path prefix in suggestion + * Don't lint on `Option` and `Result` + * Consider `Self` prefix * [`explicit_deref_methods`]: Also lint on chained `deref` calls [#6865](https://github.com/rust-lang/rust-clippy/pull/6865) * [`or_fun_call`]: Also lint on `unsafe` blocks @@ -3959,6 +3957,7 @@ Released 2021-05-06 [#6782](https://github.com/rust-lang/rust-clippy/pull/6782) ### Others + * Running `cargo clippy` after `cargo check` now works as expected (`cargo clippy` and `cargo check` no longer shares the same build cache) [#6687](https://github.com/rust-lang/rust-clippy/pull/6687) @@ -4174,7 +4173,6 @@ Released 2021-02-11 * [`field_reassign_with_default`] No longer lint for private fields [#6537](https://github.com/rust-lang/rust-clippy/pull/6537) - ### Suggestion Fixes/Improvements * [`vec_box`]: Provide correct type scope suggestion @@ -4319,8 +4317,8 @@ Released 2020-12-31 ### Documentation Improvements * Some doc improvements: - * [`rc_buffer`] [#6090](https://github.com/rust-lang/rust-clippy/pull/6090) - * [`empty_loop`] [#6162](https://github.com/rust-lang/rust-clippy/pull/6162) + * [`rc_buffer`] [#6090](https://github.com/rust-lang/rust-clippy/pull/6090) + * [`empty_loop`] [#6162](https://github.com/rust-lang/rust-clippy/pull/6162) * [`doc_markdown`]: Document problematic link text style [#6107](https://github.com/rust-lang/rust-clippy/pull/6107) @@ -4703,7 +4701,6 @@ Released 2020-06-04 * [`fn_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294) * [`vtable_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294) - ### Moves and Deprecations * Deprecate [`replace_consts`] lint [#5380](https://github.com/rust-lang/rust-clippy/pull/5380) @@ -4718,7 +4715,7 @@ Released 2020-06-04 ### Enhancements -* On _nightly_ you can now use `cargo clippy --fix -Z unstable-options` to +* On *nightly* you can now use `cargo clippy --fix -Z unstable-options` to auto-fix lints that support this [#5363](https://github.com/rust-lang/rust-clippy/pull/5363) * Make [`redundant_clone`] also trigger on cases where the cloned value is not consumed. [#5304](https://github.com/rust-lang/rust-clippy/pull/5304) @@ -4745,7 +4742,6 @@ Released 2020-06-04 * [`redundant_pattern`] [#5287](https://github.com/rust-lang/rust-clippy/pull/5287) * [`inconsistent_digit_grouping`] [#5451](https://github.com/rust-lang/rust-clippy/pull/5451) - ### Suggestion Improvements * Improved [`question_mark`] lint suggestion so that it doesn't add redundant `as_ref()` [#5481](https://github.com/rust-lang/rust-clippy/pull/5481) @@ -4823,7 +4819,6 @@ Released 2020-04-23 * Clippy now completely runs on GitHub Actions [#5190](https://github.com/rust-lang/rust-clippy/pull/5190) - ## Rust 1.42 Released 2020-03-12 @@ -4890,7 +4885,6 @@ Released 2020-03-12 * Improve documentation of [`empty_enum`], [`replace_consts`], [`redundant_clone`], and [`iterator_step_by_zero`] - ## Rust 1.41 Released 2020-01-30 @@ -5106,7 +5100,6 @@ Released 2019-07-04 * Fix ICE in [`suspicious_else_formatting`] [#3960](https://github.com/rust-lang/rust-clippy/pull/3960) * Fix ICE in [`decimal_literal_representation`] [#3931](https://github.com/rust-lang/rust-clippy/pull/3931) - ## Rust 1.35 Released 2019-05-20 @@ -5129,7 +5122,7 @@ Released 2019-05-20 * Fix false positive in [`needless_continue`] pertaining to loop labels * Fix false positive for [`boxed_local`] pertaining to arguments moved into closures * Fix false positive for [`use_self`] in nested functions -* Fix suggestion for [`expect_fun_call`] (https://github.com/rust-lang/rust-clippy/pull/3846) +* Fix suggestion for [`expect_fun_call`] () * Fix suggestion for [`explicit_counter_loop`] to deal with parenthesizing range variables * Fix suggestion for [`single_char_pattern`] to correctly escape single quotes * Avoid triggering [`redundant_closure`] in macros @@ -5273,6 +5266,7 @@ Released 2018-12-06 Released 2018-10-25 [View all 88 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2018-08-02T16%3A54%3A12Z..2018-09-17T09%3A44%3A06Z+base%3Amaster) + * Deprecate `assign_ops` lint * New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`], [`needless_collect`], [`copy_iterator`] @@ -5322,255 +5316,326 @@ Released 2018-09-13 * Improve website header ## 0.0.212 (2018-07-10) + * Rustup to *rustc 1.29.0-nightly (e06c87544 2018-07-06)* ## 0.0.211 + * Rustup to *rustc 1.28.0-nightly (e3bf634e0 2018-06-28)* ## 0.0.210 + * Rustup to *rustc 1.28.0-nightly (01cc982e9 2018-06-24)* ## 0.0.209 + * Rustup to *rustc 1.28.0-nightly (523097979 2018-06-18)* ## 0.0.208 + * Rustup to *rustc 1.28.0-nightly (86a8f1a63 2018-06-17)* ## 0.0.207 + * Rustup to *rustc 1.28.0-nightly (2a0062974 2018-06-09)* ## 0.0.206 + * Rustup to *rustc 1.28.0-nightly (5bf68db6e 2018-05-28)* ## 0.0.205 + * Rustup to *rustc 1.28.0-nightly (990d8aa74 2018-05-25)* * Rename `unused_lifetimes` to `extra_unused_lifetimes` because of naming conflict with new rustc lint ## 0.0.204 + * Rustup to *rustc 1.28.0-nightly (71e87be38 2018-05-22)* ## 0.0.203 + * Rustup to *rustc 1.28.0-nightly (a3085756e 2018-05-19)* * Clippy attributes are now of the form `clippy::cyclomatic_complexity` instead of `clippy(cyclomatic_complexity)` ## 0.0.202 + * Rustup to *rustc 1.28.0-nightly (952f344cd 2018-05-18)* ## 0.0.201 + * Rustup to *rustc 1.27.0-nightly (2f2a11dfc 2018-05-16)* ## 0.0.200 + * Rustup to *rustc 1.27.0-nightly (9fae15374 2018-05-13)* ## 0.0.199 + * Rustup to *rustc 1.27.0-nightly (ff2ac35db 2018-05-12)* ## 0.0.198 + * Rustup to *rustc 1.27.0-nightly (acd3871ba 2018-05-10)* ## 0.0.197 + * Rustup to *rustc 1.27.0-nightly (428ea5f6b 2018-05-06)* ## 0.0.196 + * Rustup to *rustc 1.27.0-nightly (e82261dfb 2018-05-03)* ## 0.0.195 + * Rustup to *rustc 1.27.0-nightly (ac3c2288f 2018-04-18)* ## 0.0.194 + * Rustup to *rustc 1.27.0-nightly (bd40cbbe1 2018-04-14)* * New lints: [`cast_ptr_alignment`], [`transmute_ptr_to_ptr`], [`write_literal`], [`write_with_newline`], [`writeln_empty_string`] ## 0.0.193 + * Rustup to *rustc 1.27.0-nightly (eeea94c11 2018-04-06)* ## 0.0.192 + * Rustup to *rustc 1.27.0-nightly (fb44b4c0e 2018-04-04)* * New lint: [`print_literal`] ## 0.0.191 + * Rustup to *rustc 1.26.0-nightly (ae544ee1c 2018-03-29)* * Lint audit; categorize lints as style, correctness, complexity, pedantic, nursery, restriction. ## 0.0.190 + * Fix a bunch of intermittent cargo bugs ## 0.0.189 + * Rustup to *rustc 1.26.0-nightly (5508b2714 2018-03-18)* ## 0.0.188 + * Rustup to *rustc 1.26.0-nightly (392645394 2018-03-15)* * New lint: [`while_immutable_condition`] ## 0.0.187 + * Rustup to *rustc 1.26.0-nightly (322d7f7b9 2018-02-25)* * New lints: [`redundant_field_names`], [`suspicious_arithmetic_impl`], [`suspicious_op_assign_impl`] ## 0.0.186 + * Rustup to *rustc 1.25.0-nightly (0c6091fbd 2018-02-04)* * Various false positive fixes ## 0.0.185 + * Rustup to *rustc 1.25.0-nightly (56733bc9f 2018-02-01)* * New lint: [`question_mark`] ## 0.0.184 + * Rustup to *rustc 1.25.0-nightly (90eb44a58 2018-01-29)* * New lints: [`double_comparisons`], [`empty_line_after_outer_attr`] ## 0.0.183 + * Rustup to *rustc 1.25.0-nightly (21882aad7 2018-01-28)* * New lint: [`misaligned_transmute`] ## 0.0.182 + * Rustup to *rustc 1.25.0-nightly (a0dcecff9 2018-01-24)* * New lint: [`decimal_literal_representation`] ## 0.0.181 + * Rustup to *rustc 1.25.0-nightly (97520ccb1 2018-01-21)* * New lints: [`else_if_without_else`], [`option_option`], [`unit_arg`], [`unnecessary_fold`] * Removed `unit_expr` * Various false positive fixes for [`needless_pass_by_value`] ## 0.0.180 + * Rustup to *rustc 1.25.0-nightly (3f92e8d89 2018-01-14)* ## 0.0.179 + * Rustup to *rustc 1.25.0-nightly (61452e506 2018-01-09)* ## 0.0.178 + * Rustup to *rustc 1.25.0-nightly (ee220daca 2018-01-07)* ## 0.0.177 + * Rustup to *rustc 1.24.0-nightly (250b49205 2017-12-21)* * New lint: [`match_as_ref`] ## 0.0.176 + * Rustup to *rustc 1.24.0-nightly (0077d128d 2017-12-14)* ## 0.0.175 + * Rustup to *rustc 1.24.0-nightly (bb42071f6 2017-12-01)* ## 0.0.174 + * Rustup to *rustc 1.23.0-nightly (63739ab7b 2017-11-21)* ## 0.0.173 + * Rustup to *rustc 1.23.0-nightly (33374fa9d 2017-11-20)* ## 0.0.172 + * Rustup to *rustc 1.23.0-nightly (d0f8e2913 2017-11-16)* ## 0.0.171 + * Rustup to *rustc 1.23.0-nightly (ff0f5de3b 2017-11-14)* ## 0.0.170 + * Rustup to *rustc 1.23.0-nightly (d6b06c63a 2017-11-09)* ## 0.0.169 + * Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)* * New lints: [`just_underscores_and_digits`], `result_map_unwrap_or_else`, [`transmute_bytes_to_str`] ## 0.0.168 + * Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)* ## 0.0.167 + * Rustup to *rustc 1.23.0-nightly (90ef3372e 2017-10-29)* * New lints: `const_static_lifetime`, [`erasing_op`], [`fallible_impl_from`], [`println_empty_string`], [`useless_asref`] ## 0.0.166 + * Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)* * New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], `invalid_ref`, [`option_map_or_none`], [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`], [`transmute_int_to_float`] ## 0.0.165 + * Rust upgrade to rustc 1.22.0-nightly (0e6f4cf51 2017-09-27) * New lint: [`mut_range_bound`] ## 0.0.164 + * Update to *rustc 1.22.0-nightly (6c476ce46 2017-09-25)* * New lint: [`int_plus_one`] ## 0.0.163 + * Update to *rustc 1.22.0-nightly (14039a42a 2017-09-22)* ## 0.0.162 + * Update to *rustc 1.22.0-nightly (0701b37d9 2017-09-18)* * New lint: [`chars_last_cmp`] * Improved suggestions for [`needless_borrow`], [`ptr_arg`], ## 0.0.161 + * Update to *rustc 1.22.0-nightly (539f2083d 2017-09-13)* ## 0.0.160 + * Update to *rustc 1.22.0-nightly (dd08c3070 2017-09-12)* ## 0.0.159 + * Update to *rustc 1.22.0-nightly (eba374fb2 2017-09-11)* * New lint: [`clone_on_ref_ptr`] ## 0.0.158 + * New lint: [`manual_memcpy`] * [`cast_lossless`] no longer has redundant parentheses in its suggestions * Update to *rustc 1.22.0-nightly (dead08cb3 2017-09-08)* ## 0.0.157 - 2017-09-04 + * Update to *rustc 1.22.0-nightly (981ce7d8d 2017-09-03)* * New lint: `unit_expr` ## 0.0.156 - 2017-09-03 + * Update to *rustc 1.22.0-nightly (744dd6c1d 2017-09-02)* ## 0.0.155 + * Update to *rustc 1.21.0-nightly (c11f689d2 2017-08-29)* * New lint: [`infinite_iter`], [`maybe_infinite_iter`], [`cast_lossless`] ## 0.0.154 + * Update to *rustc 1.21.0-nightly (2c0558f63 2017-08-24)* * Fix [`use_self`] triggering inside derives * Add support for linting an entire workspace with `cargo clippy --all` * New lint: [`naive_bytecount`] ## 0.0.153 + * Update to *rustc 1.21.0-nightly (8c303ed87 2017-08-20)* * New lint: [`use_self`] ## 0.0.152 + * Update to *rustc 1.21.0-nightly (df511d554 2017-08-14)* ## 0.0.151 + * Update to *rustc 1.21.0-nightly (13d94d5fa 2017-08-10)* ## 0.0.150 + * Update to *rustc 1.21.0-nightly (215e0b10e 2017-08-08)* ## 0.0.148 + * Update to *rustc 1.21.0-nightly (37c7d0ebb 2017-07-31)* * New lints: [`unreadable_literal`], [`inconsistent_digit_grouping`], [`large_digit_groups`] ## 0.0.147 + * Update to *rustc 1.21.0-nightly (aac223f4f 2017-07-30)* ## 0.0.146 + * Update to *rustc 1.21.0-nightly (52a330969 2017-07-27)* * Fixes false positives in `inline_always` * Fixes false negatives in `panic_params` ## 0.0.145 + * Update to *rustc 1.20.0-nightly (afe145d22 2017-07-23)* ## 0.0.144 + * Update to *rustc 1.20.0-nightly (086eaa78e 2017-07-15)* ## 0.0.143 + * Update to *rustc 1.20.0-nightly (d84693b93 2017-07-09)* * Fix `cargo clippy` crashing on `dylib` projects * Fix false positives around `nested_while_let` and `never_loop` ## 0.0.142 + * Update to *rustc 1.20.0-nightly (067971139 2017-07-02)* ## 0.0.141 + * Rewrite of the `doc_markdown` lint. * Deprecated [`range_step_by_zero`] * New lint: [`iterator_step_by_zero`] @@ -5578,151 +5643,195 @@ Released 2018-09-13 * Update to *rustc 1.20.0-nightly (69c65d296 2017-06-28)* ## 0.0.140 - 2017-06-16 + * Update to *rustc 1.19.0-nightly (258ae6dd9 2017-06-15)* ## 0.0.139 — 2017-06-10 + * Update to *rustc 1.19.0-nightly (4bf5c99af 2017-06-10)* * Fix bugs with for loop desugaring * Check for [`AsRef`]/[`AsMut`] arguments in [`wrong_self_convention`] ## 0.0.138 — 2017-06-05 + * Update to *rustc 1.19.0-nightly (0418fa9d3 2017-06-04)* ## 0.0.137 — 2017-06-05 + * Update to *rustc 1.19.0-nightly (6684d176c 2017-06-03)* ## 0.0.136 — 2017—05—26 + * Update to *rustc 1.19.0-nightly (557967766 2017-05-26)* ## 0.0.135 — 2017—05—24 + * Update to *rustc 1.19.0-nightly (5b13bff52 2017-05-23)* ## 0.0.134 — 2017—05—19 + * Update to *rustc 1.19.0-nightly (0ed1ec9f9 2017-05-18)* ## 0.0.133 — 2017—05—14 + * Update to *rustc 1.19.0-nightly (826d8f385 2017-05-13)* ## 0.0.132 — 2017—05—05 + * Fix various bugs and some ices ## 0.0.131 — 2017—05—04 + * Update to *rustc 1.19.0-nightly (2d4ed8e0c 2017-05-03)* ## 0.0.130 — 2017—05—03 + * Update to *rustc 1.19.0-nightly (6a5fc9eec 2017-05-02)* ## 0.0.129 — 2017-05-01 + * Update to *rustc 1.19.0-nightly (06fb4d256 2017-04-30)* ## 0.0.128 — 2017-04-28 + * Update to *rustc 1.18.0-nightly (94e884b63 2017-04-27)* ## 0.0.127 — 2017-04-27 + * Update to *rustc 1.18.0-nightly (036983201 2017-04-26)* * New lint: [`needless_continue`] ## 0.0.126 — 2017-04-24 + * Update to *rustc 1.18.0-nightly (2bd4b5c6d 2017-04-23)* ## 0.0.125 — 2017-04-19 + * Update to *rustc 1.18.0-nightly (9f2abadca 2017-04-18)* ## 0.0.124 — 2017-04-16 + * Update to *rustc 1.18.0-nightly (d5cf1cb64 2017-04-15)* ## 0.0.123 — 2017-04-07 + * Fix various false positives ## 0.0.122 — 2017-04-07 + * Rustup to *rustc 1.18.0-nightly (91ae22a01 2017-04-05)* * New lint: [`op_ref`] ## 0.0.121 — 2017-03-21 + * Rustup to *rustc 1.17.0-nightly (134c4a0f0 2017-03-20)* ## 0.0.120 — 2017-03-17 + * Rustup to *rustc 1.17.0-nightly (0aeb9c129 2017-03-15)* ## 0.0.119 — 2017-03-13 + * Rustup to *rustc 1.17.0-nightly (824c9ebbd 2017-03-12)* ## 0.0.118 — 2017-03-05 + * Rustup to *rustc 1.17.0-nightly (b1e31766d 2017-03-03)* ## 0.0.117 — 2017-03-01 + * Rustup to *rustc 1.17.0-nightly (be760566c 2017-02-28)* ## 0.0.116 — 2017-02-28 + * Fix `cargo clippy` on 64 bit windows systems ## 0.0.115 — 2017-02-27 + * Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)* * New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`] ## 0.0.114 — 2017-02-08 + * Rustup to *rustc 1.17.0-nightly (c49d10207 2017-02-07)* * Tests are now ui tests (testing the exact output of rustc) ## 0.0.113 — 2017-02-04 + * Rustup to *rustc 1.16.0-nightly (eedaa94e3 2017-02-02)* * New lint: [`large_enum_variant`] * `explicit_into_iter_loop` provides suggestions ## 0.0.112 — 2017-01-27 + * Rustup to *rustc 1.16.0-nightly (df8debf6d 2017-01-25)* ## 0.0.111 — 2017-01-21 + * Rustup to *rustc 1.16.0-nightly (a52da95ce 2017-01-20)* ## 0.0.110 — 2017-01-20 + * Add badges and categories to `Cargo.toml` ## 0.0.109 — 2017-01-19 + * Update to *rustc 1.16.0-nightly (c07a6ae77 2017-01-17)* ## 0.0.108 — 2017-01-12 + * Update to *rustc 1.16.0-nightly (2782e8f8f 2017-01-12)* ## 0.0.107 — 2017-01-11 + * Update regex dependency * Fix FP when matching `&&mut` by `&ref` * Reintroduce `for (_, x) in &mut hash_map` -> `for x in hash_map.values_mut()` * New lints: [`unused_io_amount`], [`forget_ref`], [`short_circuit_statement`] ## 0.0.106 — 2017-01-04 + * Fix FP introduced by rustup in [`wrong_self_convention`] ## 0.0.105 — 2017-01-04 + * Update to *rustc 1.16.0-nightly (468227129 2017-01-03)* * New lints: [`deref_addrof`], [`double_parens`], [`pub_enum_variant_names`] * Fix suggestion in [`new_without_default`] * FP fix in [`absurd_extreme_comparisons`] ## 0.0.104 — 2016-12-15 + * Update to *rustc 1.15.0-nightly (8f02c429a 2016-12-15)* ## 0.0.103 — 2016-11-25 + * Update to *rustc 1.15.0-nightly (d5814b03e 2016-11-23)* ## 0.0.102 — 2016-11-24 + * Update to *rustc 1.15.0-nightly (3bf2be9ce 2016-11-22)* ## 0.0.101 — 2016-11-23 + * Update to *rustc 1.15.0-nightly (7b3eeea22 2016-11-21)* * New lint: [`string_extend_chars`] ## 0.0.100 — 2016-11-20 + * Update to *rustc 1.15.0-nightly (ac635aa95 2016-11-18)* ## 0.0.99 — 2016-11-18 + * Update to rustc 1.15.0-nightly (0ed951993 2016-11-14) * New lint: [`get_unwrap`] ## 0.0.98 — 2016-11-08 + * Fixes an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy` ## 0.0.97 — 2016-11-03 + * For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was previously added for a short time under the name `clippy` but removed for compatibility. @@ -5731,34 +5840,43 @@ Released 2018-09-13 * New lints: [`if_let_redundant_pattern_matching`], [`partialeq_ne_impl`] ## 0.0.96 — 2016-10-22 + * Rustup to *rustc 1.14.0-nightly (f09420685 2016-10-20)* * New lint: [`iter_skip_next`] ## 0.0.95 — 2016-10-06 + * Rustup to *rustc 1.14.0-nightly (3210fd5c2 2016-10-05)* ## 0.0.94 — 2016-10-04 + * Fixes bustage on Windows due to forbidden directory name ## 0.0.93 — 2016-10-03 + * Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)* * `option_map_unwrap_or` and `option_map_unwrap_or_else` are now allowed by default. * New lint: [`explicit_into_iter_loop`] ## 0.0.92 — 2016-09-30 + * Rustup to *rustc 1.14.0-nightly (289f3a4ca 2016-09-29)* ## 0.0.91 — 2016-09-28 + * Rustup to *rustc 1.13.0-nightly (d0623cf7b 2016-09-26)* ## 0.0.90 — 2016-09-09 + * Rustup to *rustc 1.13.0-nightly (f1f40f850 2016-09-09)* ## 0.0.89 — 2016-09-06 + * Rustup to *rustc 1.13.0-nightly (cbe4de78e 2016-09-05)* ## 0.0.88 — 2016-09-04 + * Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)* * The following lints are not new but were only usable through the `clippy` lint groups: [`filter_next`], `for_loop_over_option`, @@ -5767,30 +5885,37 @@ Released 2018-09-13 through `cargo clippy`. ## 0.0.87 — 2016-08-31 + * Rustup to *rustc 1.13.0-nightly (eac41469d 2016-08-30)* * New lints: [`builtin_type_shadow`] * Fix FP in [`zero_prefixed_literal`] and `0b`/`0o` ## 0.0.86 — 2016-08-28 + * Rustup to *rustc 1.13.0-nightly (a23064af5 2016-08-27)* * New lints: [`missing_docs_in_private_items`], [`zero_prefixed_literal`] ## 0.0.85 — 2016-08-19 + * Fix ICE with [`useless_attribute`] * [`useless_attribute`] ignores `unused_imports` on `use` statements ## 0.0.84 — 2016-08-18 + * Rustup to *rustc 1.13.0-nightly (aef6971ca 2016-08-17)* ## 0.0.83 — 2016-08-17 + * Rustup to *rustc 1.12.0-nightly (1bf5fa326 2016-08-16)* * New lints: [`print_with_newline`], [`useless_attribute`] ## 0.0.82 — 2016-08-17 + * Rustup to *rustc 1.12.0-nightly (197be89f3 2016-08-15)* * New lint: [`module_inception`] ## 0.0.81 — 2016-08-14 + * Rustup to *rustc 1.12.0-nightly (1deb02ea6 2016-08-12)* * New lints: [`eval_order_dependence`], [`mixed_case_hex_literals`], [`unseparated_literal_suffix`] * False positive fix in [`too_many_arguments`] @@ -5800,14 +5925,17 @@ Released 2018-09-13 * Doc improvements ## 0.0.80 — 2016-07-31 + * Rustup to *rustc 1.12.0-nightly (1225e122f 2016-07-30)* * New lints: [`misrefactored_assign_op`], [`serde_api_misuse`] ## 0.0.79 — 2016-07-10 + * Rustup to *rustc 1.12.0-nightly (f93aaf84c 2016-07-09)* * Major suggestions refactoring ## 0.0.78 — 2016-07-02 + * Rustup to *rustc 1.11.0-nightly (01411937f 2016-07-01)* * New lints: [`wrong_transmute`], [`double_neg`], [`filter_map`] * For compatibility, `cargo clippy` does not defines the `clippy` feature @@ -5815,118 +5943,148 @@ Released 2018-09-13 * [`collapsible_if`] now considers `if let` ## 0.0.77 — 2016-06-21 + * Rustup to *rustc 1.11.0-nightly (5522e678b 2016-06-20)* * New lints: `stutter` and [`iter_nth`] ## 0.0.76 — 2016-06-10 + * Rustup to *rustc 1.11.0-nightly (7d2f75a95 2016-06-09)* * `cargo clippy` now automatically defines the `clippy` feature * New lint: [`not_unsafe_ptr_arg_deref`] ## 0.0.75 — 2016-06-08 + * Rustup to *rustc 1.11.0-nightly (763f9234b 2016-06-06)* ## 0.0.74 — 2016-06-07 + * Fix bug with `cargo-clippy` JSON parsing * Add the `CLIPPY_DISABLE_DOCS_LINKS` environment variable to deactivate the “for further information visit *lint-link*” message. ## 0.0.73 — 2016-06-05 + * Fix false positives in [`useless_let_if_seq`] ## 0.0.72 — 2016-06-04 + * Fix false positives in [`useless_let_if_seq`] ## 0.0.71 — 2016-05-31 + * Rustup to *rustc 1.11.0-nightly (a967611d8 2016-05-30)* * New lint: [`useless_let_if_seq`] ## 0.0.70 — 2016-05-28 + * Rustup to *rustc 1.10.0-nightly (7bddce693 2016-05-27)* * [`invalid_regex`] and [`trivial_regex`] can now warn on `RegexSet::new`, `RegexBuilder::new` and byte regexes ## 0.0.69 — 2016-05-20 + * Rustup to *rustc 1.10.0-nightly (476fe6eef 2016-05-21)* * [`used_underscore_binding`] has been made `Allow` temporarily ## 0.0.68 — 2016-05-17 + * Rustup to *rustc 1.10.0-nightly (cd6a40017 2016-05-16)* * New lint: [`unnecessary_operation`] ## 0.0.67 — 2016-05-12 + * Rustup to *rustc 1.10.0-nightly (22ac88f1a 2016-05-11)* ## 0.0.66 — 2016-05-11 + * New `cargo clippy` subcommand * New lints: [`assign_op_pattern`], [`assign_ops`], [`needless_borrow`] ## 0.0.65 — 2016-05-08 + * Rustup to *rustc 1.10.0-nightly (62e2b2fb7 2016-05-06)* * New lints: [`float_arithmetic`], [`integer_arithmetic`] ## 0.0.64 — 2016-04-26 + * Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)* * New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`] ## 0.0.63 — 2016-04-08 + * Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)* ## 0.0.62 — 2016-04-07 + * Rustup to *rustc 1.9.0-nightly (bf5da36f1 2016-04-06)* ## 0.0.61 — 2016-04-03 + * Rustup to *rustc 1.9.0-nightly (5ab11d72c 2016-04-02)* * New lint: [`invalid_upcast_comparisons`] ## 0.0.60 — 2016-04-01 + * Rustup to *rustc 1.9.0-nightly (e1195c24b 2016-03-31)* ## 0.0.59 — 2016-03-31 + * Rustup to *rustc 1.9.0-nightly (30a3849f2 2016-03-30)* * New lints: [`logic_bug`], [`nonminimal_bool`] * Fixed: [`match_same_arms`] now ignores arms with guards * Improved: [`useless_vec`] now warns on `for … in vec![…]` ## 0.0.58 — 2016-03-27 + * Rustup to *rustc 1.9.0-nightly (d5a91e695 2016-03-26)* * New lint: [`doc_markdown`] ## 0.0.57 — 2016-03-27 + * Update to *rustc 1.9.0-nightly (a1e29daf1 2016-03-25)* * Deprecated lints: [`str_to_string`], [`string_to_string`], [`unstable_as_slice`], [`unstable_as_mut_slice`] * New lint: [`crosspointer_transmute`] ## 0.0.56 — 2016-03-23 + * Update to *rustc 1.9.0-nightly (0dcc413e4 2016-03-22)* * New lints: [`many_single_char_names`] and [`similar_names`] ## 0.0.55 — 2016-03-21 + * Update to *rustc 1.9.0-nightly (02310fd31 2016-03-19)* ## 0.0.54 — 2016-03-16 + * Update to *rustc 1.9.0-nightly (c66d2380a 2016-03-15)* ## 0.0.53 — 2016-03-15 + * Add a [configuration file] ## ~~0.0.52~~ ## 0.0.51 — 2016-03-13 + * Add `str` to types considered by [`len_zero`] * New lints: [`indexing_slicing`] ## 0.0.50 — 2016-03-11 + * Update to *rustc 1.9.0-nightly (c9629d61c 2016-03-10)* ## 0.0.49 — 2016-03-09 + * Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)* * New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`] ## 0.0.48 — 2016-03-07 + * Fixed: ICE in [`needless_range_loop`] with globals ## 0.0.47 — 2016-03-07 + * Update to *rustc 1.9.0-nightly (998a6720b 2016-03-07)* * New lint: [`redundant_closure_call`] @@ -6573,6 +6731,7 @@ Released 2018-09-13 [`renamed_function_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params [`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once [`repeat_vec_with_capacity`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity +[`replace_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_box [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts [`repr_packed_without_abi`]: https://rust-lang.github.io/rust-clippy/master/index.html#repr_packed_without_abi [`reserve_after_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#reserve_after_initialization diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index dd5c5dcf4d1fd..2a9c6efb52eec 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -655,6 +655,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::regex::REGEX_CREATION_IN_LOOPS_INFO, crate::regex::TRIVIAL_REGEX_INFO, crate::repeat_vec_with_capacity::REPEAT_VEC_WITH_CAPACITY_INFO, + crate::replace_box::REPLACE_BOX_INFO, crate::reserve_after_initialization::RESERVE_AFTER_INITIALIZATION_INFO, crate::return_self_not_must_use::RETURN_SELF_NOT_MUST_USE_INFO, crate::returns::LET_AND_RETURN_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e3168b8cfe02c..6d5116f1f0310 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -323,6 +323,7 @@ mod ref_patterns; mod reference; mod regex; mod repeat_vec_with_capacity; +mod replace_box; mod reserve_after_initialization; mod return_self_not_must_use; mod returns; @@ -832,5 +833,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny)); store.register_late_pass(|_| Box::new(toplevel_ref_arg::ToplevelRefArg)); store.register_late_pass(|_| Box::new(volatile_composites::VolatileComposites)); + store.register_late_pass(|_| Box::new(replace_box::ReplaceBox)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/replace_box.rs b/clippy_lints/src/replace_box.rs new file mode 100644 index 0000000000000..9388f77a839d9 --- /dev/null +++ b/clippy_lints/src/replace_box.rs @@ -0,0 +1,130 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::implements_trait; +use clippy_utils::{is_default_equivalent_call, local_is_initialized, path_def_id, path_to_local}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LangItem, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_session::declare_lint_pass; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Detects assignments of `Default::default()` or `Box::new(value)` + /// to a place of type `Box`. + /// + /// ### Why is this bad? + /// This incurs an extra heap allocation compared to assigning the boxed + /// storage. + /// + /// ### Example + /// ```no_run + /// let mut b = Box::new(1u32); + /// b = Default::default(); + /// ``` + /// Use instead: + /// ```no_run + /// let mut b = Box::new(1u32); + /// *b = Default::default(); + /// ``` + #[clippy::version = "1.92.0"] + pub REPLACE_BOX, + perf, + "assigning a newly created box to `Box` is inefficient" +} +declare_lint_pass!(ReplaceBox => [REPLACE_BOX]); + +impl LateLintPass<'_> for ReplaceBox { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + if let ExprKind::Assign(lhs, rhs, _) = &expr.kind + && !lhs.span.from_expansion() + && !rhs.span.from_expansion() + { + let lhs_ty = cx.typeck_results().expr_ty(lhs); + + // No diagnostic for late-initialized locals + if let Some(local) = path_to_local(lhs) + && !local_is_initialized(cx, local) + { + return; + } + + let Some(inner_ty) = get_box_inner_type(cx, lhs_ty) else { + return; + }; + + if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) + && implements_trait(cx, inner_ty, default_trait_id, &[]) + && is_default_call(cx, rhs) + { + span_lint_and_then( + cx, + REPLACE_BOX, + expr.span, + "creating a new box with default content", + |diag| { + let mut app = Applicability::MachineApplicable; + let suggestion = format!( + "{} = Default::default()", + Sugg::hir_with_applicability(cx, lhs, "_", &mut app).deref() + ); + + diag.note("this creates a needless allocation").span_suggestion( + expr.span, + "replace existing content with default instead", + suggestion, + app, + ); + }, + ); + } + + if inner_ty.is_sized(cx.tcx, cx.typing_env()) + && let Some(rhs_inner) = get_box_new_payload(cx, rhs) + { + span_lint_and_then(cx, REPLACE_BOX, expr.span, "creating a new box", |diag| { + let mut app = Applicability::MachineApplicable; + let suggestion = format!( + "{} = {}", + Sugg::hir_with_applicability(cx, lhs, "_", &mut app).deref(), + Sugg::hir_with_context(cx, rhs_inner, expr.span.ctxt(), "_", &mut app), + ); + + diag.note("this creates a needless allocation").span_suggestion( + expr.span, + "replace existing content with inner value instead", + suggestion, + app, + ); + }); + } + } + } +} + +fn get_box_inner_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + if let ty::Adt(def, args) = ty.kind() + && cx.tcx.is_lang_item(def.did(), LangItem::OwnedBox) + { + Some(args.type_at(0)) + } else { + None + } +} + +fn is_default_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::Call(func, _args) if is_default_equivalent_call(cx, func, Some(expr))) +} + +fn get_box_new_payload<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + if let ExprKind::Call(box_new, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_new.kind + && seg.ident.name == sym::new + && path_def_id(cx, ty).is_some_and(|id| Some(id) == cx.tcx.lang_items().owned_box()) + { + Some(arg) + } else { + None + } +} diff --git a/tests/ui/replace_box.fixed b/tests/ui/replace_box.fixed new file mode 100644 index 0000000000000..58c8ed1691d77 --- /dev/null +++ b/tests/ui/replace_box.fixed @@ -0,0 +1,72 @@ +#![warn(clippy::replace_box)] + +fn with_default(b: &mut Box) { + **b = T::default(); + //~^ replace_box +} + +fn with_sized(b: &mut Box, t: T) { + **b = t; + //~^ replace_box +} + +fn with_unsized(b: &mut Box<[u32]>) { + // No lint for assigning to Box where T: !Default + *b = Box::new([42; N]); +} + +macro_rules! create_default { + () => { + Default::default() + }; +} + +macro_rules! create_zero_box { + () => { + Box::new(0) + }; +} + +macro_rules! same { + ($v:ident) => { + $v + }; +} + +macro_rules! mac { + (three) => { + 3u32 + }; +} + +fn main() { + let mut b = Box::new(1u32); + *b = Default::default(); + //~^ replace_box + *b = Default::default(); + //~^ replace_box + + // No lint for assigning to the storage + *b = Default::default(); + *b = u32::default(); + + // No lint if either LHS or RHS originates in macro + b = create_default!(); + b = create_zero_box!(); + same!(b) = Default::default(); + + *b = 5; + //~^ replace_box + + *b = mac!(three); + //~^ replace_box + + // No lint for assigning to Box where T: !Default + let mut b = Box::::from("hi".to_string()); + b = Default::default(); + + // No lint for late initializations + #[allow(clippy::needless_late_init)] + let bb: Box; + bb = Default::default(); +} diff --git a/tests/ui/replace_box.rs b/tests/ui/replace_box.rs new file mode 100644 index 0000000000000..e1fb223e4f211 --- /dev/null +++ b/tests/ui/replace_box.rs @@ -0,0 +1,72 @@ +#![warn(clippy::replace_box)] + +fn with_default(b: &mut Box) { + *b = Box::new(T::default()); + //~^ replace_box +} + +fn with_sized(b: &mut Box, t: T) { + *b = Box::new(t); + //~^ replace_box +} + +fn with_unsized(b: &mut Box<[u32]>) { + // No lint for assigning to Box where T: !Default + *b = Box::new([42; N]); +} + +macro_rules! create_default { + () => { + Default::default() + }; +} + +macro_rules! create_zero_box { + () => { + Box::new(0) + }; +} + +macro_rules! same { + ($v:ident) => { + $v + }; +} + +macro_rules! mac { + (three) => { + 3u32 + }; +} + +fn main() { + let mut b = Box::new(1u32); + b = Default::default(); + //~^ replace_box + b = Box::default(); + //~^ replace_box + + // No lint for assigning to the storage + *b = Default::default(); + *b = u32::default(); + + // No lint if either LHS or RHS originates in macro + b = create_default!(); + b = create_zero_box!(); + same!(b) = Default::default(); + + b = Box::new(5); + //~^ replace_box + + b = Box::new(mac!(three)); + //~^ replace_box + + // No lint for assigning to Box where T: !Default + let mut b = Box::::from("hi".to_string()); + b = Default::default(); + + // No lint for late initializations + #[allow(clippy::needless_late_init)] + let bb: Box; + bb = Default::default(); +} diff --git a/tests/ui/replace_box.stderr b/tests/ui/replace_box.stderr new file mode 100644 index 0000000000000..7d9c85da17314 --- /dev/null +++ b/tests/ui/replace_box.stderr @@ -0,0 +1,52 @@ +error: creating a new box + --> tests/ui/replace_box.rs:4:5 + | +LL | *b = Box::new(T::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `**b = T::default()` + | + = note: this creates a needless allocation + = note: `-D clippy::replace-box` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::replace_box)]` + +error: creating a new box + --> tests/ui/replace_box.rs:9:5 + | +LL | *b = Box::new(t); + | ^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `**b = t` + | + = note: this creates a needless allocation + +error: creating a new box with default content + --> tests/ui/replace_box.rs:44:5 + | +LL | b = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: replace existing content with default instead: `*b = Default::default()` + | + = note: this creates a needless allocation + +error: creating a new box with default content + --> tests/ui/replace_box.rs:46:5 + | +LL | b = Box::default(); + | ^^^^^^^^^^^^^^^^^^ help: replace existing content with default instead: `*b = Default::default()` + | + = note: this creates a needless allocation + +error: creating a new box + --> tests/ui/replace_box.rs:58:5 + | +LL | b = Box::new(5); + | ^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `*b = 5` + | + = note: this creates a needless allocation + +error: creating a new box + --> tests/ui/replace_box.rs:61:5 + | +LL | b = Box::new(mac!(three)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `*b = mac!(three)` + | + = note: this creates a needless allocation + +error: aborting due to 6 previous errors + From 0415d96f1e0e20e6d5d92577857ebc719e08004e Mon Sep 17 00:00:00 2001 From: yanglsh Date: Sun, 24 Aug 2025 02:27:18 +0800 Subject: [PATCH 19/89] Migrate `needless_continue` to late pass --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/loops/mod.rs | 5 +- clippy_lints/src/needless_continue.rs | 222 ++++++++++++++++++-------- clippy_utils/src/higher.rs | 11 +- 4 files changed, 166 insertions(+), 74 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e3168b8cfe02c..bbc7c1f42afa2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -618,7 +618,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements)); store.register_early_pass(|| Box::new(precedence::Precedence)); store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals)); - store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue)); + store.register_late_pass(|_| Box::new(needless_continue::NeedlessContinue)); store.register_early_pass(|| Box::new(redundant_else::RedundantElse)); store.register_late_pass(|_| Box::new(create_dir::CreateDir)); store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType)); diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index b5d6da478d8a3..a064a5910ef98 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -871,7 +871,10 @@ impl<'tcx> LateLintPass<'tcx> for Loops { while_let_on_iterator::check(cx, expr); - if let Some(higher::While { condition, body, span }) = higher::While::hir(expr) { + if let Some(higher::While { + condition, body, span, .. + }) = higher::While::hir(expr) + { while_immutable_condition::check(cx, condition, body); while_float::check(cx, condition); missing_spin_loop::check(cx, condition, body); diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index b8601f77e2492..2097038e1fde8 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -1,7 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::source::{indent_of, snippet, snippet_block}; -use rustc_ast::{Block, Label, ast}; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use clippy_utils::source::{indent_of, snippet, snippet_block, snippet_with_context}; +use clippy_utils::{higher, is_from_proc_macro}; +use rustc_ast::Label; +use rustc_errors::Applicability; +use rustc_hir::{Block, Expr, ExprKind, LoopSource, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -127,9 +130,9 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessContinue => [NEEDLESS_CONTINUE]); -impl EarlyLintPass for NeedlessContinue { - fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - if !expr.span.from_expansion() { +impl<'tcx> LateLintPass<'tcx> for NeedlessContinue { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if !expr.span.in_external_macro(cx.sess().source_map()) && !is_from_proc_macro(cx, expr) { check_and_warn(cx, expr); } } @@ -188,19 +191,19 @@ impl EarlyLintPass for NeedlessContinue { /// /// - The expression is a `continue` node. /// - The expression node is a block with the first statement being a `continue`. -fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&Label>) -> bool { +fn needless_continue_in_else(else_expr: &Expr<'_>, label: Option<&Label>) -> bool { match else_expr.kind { - ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label), - ast::ExprKind::Continue(l) => compare_labels(label, l.as_ref()), + ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label), + ExprKind::Continue(l) => compare_labels(label, l.label.as_ref()), _ => false, } } -fn is_first_block_stmt_continue(block: &Block, label: Option<&Label>) -> bool { +fn is_first_block_stmt_continue(block: &Block<'_>, label: Option<&Label>) -> bool { block.stmts.first().is_some_and(|stmt| match stmt.kind { - ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => { - if let ast::ExprKind::Continue(ref l) = e.kind { - compare_labels(label, l.as_ref()) + StmtKind::Semi(ref e) | StmtKind::Expr(ref e) => { + if let ExprKind::Continue(ref l) = e.kind { + compare_labels(label, l.label.as_ref()) } else { false } @@ -223,19 +226,33 @@ fn compare_labels(loop_label: Option<&Label>, continue_label: Option<&Label>) -> /// If `expr` is a loop expression (while/while let/for/loop), calls `func` with /// the AST object representing the loop block of `expr`. -fn with_loop_block(expr: &ast::Expr, mut func: F) +fn with_loop_block<'tcx, F>(expr: &Expr<'tcx>, mut func: F) where - F: FnMut(&Block, Option<&Label>), + F: FnMut(&Block<'tcx>, Option<&Label>), { - if let ast::ExprKind::While(_, loop_block, label) - | ast::ExprKind::ForLoop { - body: loop_block, - label, - .. + if let Some(higher::ForLoop { body, label, .. }) = higher::ForLoop::hir(expr) + && let ExprKind::Block(block, _) = &body.kind + { + func(block, label.as_ref()); + return; } - | ast::ExprKind::Loop(loop_block, label, ..) = &expr.kind + + if let Some(higher::While { body, label, .. }) = higher::While::hir(expr) + && let ExprKind::Block(block, _) = &body.kind { - func(loop_block, label.as_ref()); + func(block, label.as_ref()); + return; + } + + if let Some(higher::WhileLet { if_then, label, .. }) = higher::WhileLet::hir(expr) + && let ExprKind::Block(block, _) = &if_then.kind + { + func(block, label.as_ref()); + return; + } + + if let ExprKind::Loop(block, label, LoopSource::Loop, ..) = expr.kind { + func(block, label.as_ref()); } } @@ -247,17 +264,18 @@ where /// - The `if` condition expression, /// - The `then` block, and /// - The `else` expression. -fn with_if_expr(stmt: &ast::Stmt, mut func: F) +fn with_if_expr<'tcx, F>(expr: &Expr<'tcx>, mut func: F) where - F: FnMut(&ast::Expr, &ast::Expr, &Block, &ast::Expr), + F: FnMut(&Expr<'tcx>, &Expr<'tcx>, &Block<'tcx>, &Expr<'tcx>), { - match stmt.kind { - ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => { - if let ast::ExprKind::If(ref cond, ref if_block, Some(ref else_expr)) = e.kind { - func(e, cond, if_block, else_expr); - } - }, - _ => {}, + if let Some(higher::If { + cond, + then, + r#else: Some(r#else), + }) = higher::If::hir(expr) + && let ExprKind::Block(then, _) = then.kind + { + func(expr, cond, then, r#else); } } @@ -269,20 +287,21 @@ enum LintType { } /// Data we pass around for construction of help messages. -struct LintData<'a> { +#[derive(Debug)] +struct LintData<'tcx> { /// The `if` expression encountered in the above loop. - if_expr: &'a ast::Expr, + if_expr: &'tcx Expr<'tcx>, /// The condition expression for the above `if`. - if_cond: &'a ast::Expr, + if_cond: &'tcx Expr<'tcx>, /// The `then` block of the `if` statement. - if_block: &'a Block, + if_block: &'tcx Block<'tcx>, /// The `else` block of the `if` statement. /// Note that we only work with `if` exprs that have an `else` branch. - else_expr: &'a ast::Expr, + else_expr: &'tcx Expr<'tcx>, /// The 0-based index of the `if` statement in the containing loop block. - stmt_idx: usize, + stmt_idx: Option, /// The statements of the loop block. - loop_block: &'a Block, + loop_block: &'tcx Block<'tcx>, } const MSG_REDUNDANT_CONTINUE_EXPRESSION: &str = "this `continue` expression is redundant"; @@ -299,7 +318,7 @@ const DROP_ELSE_BLOCK_MSG: &str = "consider dropping the `else` clause"; const DROP_CONTINUE_EXPRESSION_MSG: &str = "consider dropping the `continue` expression"; -fn emit_warning(cx: &EarlyContext<'_>, data: &LintData<'_>, header: &str, typ: LintType) { +fn emit_warning(cx: &LateContext<'_>, data: &LintData<'_>, header: &str, typ: LintType) { // snip is the whole *help* message that appears after the warning. // message is the warning message. // expr is the expression which the lint warning message refers to. @@ -325,8 +344,15 @@ fn emit_warning(cx: &EarlyContext<'_>, data: &LintData<'_>, header: &str, typ: L ); } -fn suggestion_snippet_for_continue_inside_if(cx: &EarlyContext<'_>, data: &LintData<'_>) -> String { - let cond_code = snippet(cx, data.if_cond.span, ".."); +fn suggestion_snippet_for_continue_inside_if(cx: &LateContext<'_>, data: &LintData<'_>) -> String { + let mut applicability = Applicability::MachineApplicable; + let (cond_code, _) = snippet_with_context( + cx, + data.if_cond.span, + data.if_expr.span.ctxt(), + "..", + &mut applicability, + ); let continue_code = snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span)); @@ -339,7 +365,7 @@ fn suggestion_snippet_for_continue_inside_if(cx: &EarlyContext<'_>, data: &LintD ) } -fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &LintData<'_>) -> String { +fn suggestion_snippet_for_continue_inside_else(cx: &LateContext<'_>, data: &LintData<'_>) -> String { let cond_code = snippet(cx, data.if_cond.span, ".."); // Region B @@ -352,18 +378,32 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin let indent = span_of_first_expr_in_block(data.if_block) .and_then(|span| indent_of(cx, span)) .unwrap_or(0); - let to_annex = data.loop_block.stmts[data.stmt_idx + 1..] - .iter() - .map(|stmt| { - let span = cx.sess().source_map().stmt_span(stmt.span, data.loop_block.span); + let to_annex = if let Some(stmt_idx) = data.stmt_idx { + let mut lines = data.loop_block.stmts[stmt_idx + 1..] + .iter() + .map(|stmt| { + let span = cx.sess().source_map().stmt_span(stmt.span, data.loop_block.span); + let snip = snippet_block(cx, span, "..", None); + snip.lines() + .map(|line| format!("{}{line}", " ".repeat(indent))) + .collect::>() + .join("\n") + }) + .collect::>(); + if let Some(expr) = data.loop_block.expr { + let span = expr.span; let snip = snippet_block(cx, span, "..", None); - snip.lines() + let expr_lines = snip + .lines() .map(|line| format!("{}{line}", " ".repeat(indent))) .collect::>() - .join("\n") - }) - .collect::>() - .join("\n"); + .join("\n"); + lines.push(expr_lines); + } + lines.join("\n") + } else { + "".to_string() + }; let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0); format!( @@ -373,46 +413,49 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin ) } -fn check_last_stmt_in_expr(inner_expr: &ast::Expr, func: &F) +fn check_last_stmt_in_expr(inner_expr: &Expr<'_>, func: &F) where F: Fn(Option<&Label>, Span), { match &inner_expr.kind { - ast::ExprKind::Continue(continue_label) => { - func(continue_label.as_ref(), inner_expr.span); + ExprKind::Continue(continue_label) => { + func(continue_label.label.as_ref(), inner_expr.span); }, - ast::ExprKind::If(_, then_block, else_block) => { + ExprKind::If(_, then_block, else_block) if let ExprKind::Block(then_block, _) = then_block.kind => { check_last_stmt_in_block(then_block, func); if let Some(else_block) = else_block { check_last_stmt_in_expr(else_block, func); } }, - ast::ExprKind::Match(_, arms, _) => { - for arm in arms { - if let Some(expr) = &arm.body { - check_last_stmt_in_expr(expr, func); - } + ExprKind::Match(_, arms, _) => { + for arm in arms.iter() { + check_last_stmt_in_expr(arm.body, func); } }, - ast::ExprKind::Block(b, _) => { + ExprKind::Block(b, _) => { check_last_stmt_in_block(b, func); }, _ => {}, } } -fn check_last_stmt_in_block(b: &Block, func: &F) +fn check_last_stmt_in_block(b: &Block<'_>, func: &F) where F: Fn(Option<&Label>, Span), { + if let Some(expr) = b.expr { + check_last_stmt_in_expr(expr, func); + return; + } + if let Some(last_stmt) = b.stmts.last() - && let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind + && let StmtKind::Expr(inner_expr) | StmtKind::Semi(inner_expr) = last_stmt.kind { check_last_stmt_in_expr(inner_expr, func); } } -fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { +fn check_and_warn(cx: &LateContext<'_>, expr: &Expr<'_>) { with_loop_block(expr, |loop_block, label| { let p = |continue_label: Option<&Label>, span: Span| { if compare_labels(label, continue_label) { @@ -427,16 +470,51 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { } }; - let stmts = &loop_block.stmts; + let stmts = loop_block.stmts; for (i, stmt) in stmts.iter().enumerate() { let mut maybe_emitted_in_if = false; - with_if_expr(stmt, |if_expr, cond, then_block, else_expr| { + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind { + with_if_expr(expr, |if_expr, cond, then_block, else_expr| { + let data = &LintData { + if_expr, + if_cond: cond, + if_block: then_block, + else_expr, + stmt_idx: Some(i), + loop_block, + }; + + maybe_emitted_in_if = true; + if needless_continue_in_else(else_expr, label) { + emit_warning( + cx, + data, + DROP_ELSE_BLOCK_AND_MERGE_MSG, + LintType::ContinueInsideElseBlock, + ); + } else if is_first_block_stmt_continue(then_block, label) { + emit_warning(cx, data, DROP_ELSE_BLOCK_MSG, LintType::ContinueInsideThenBlock); + } else { + maybe_emitted_in_if = false; + } + }); + } + + if i == stmts.len() - 1 && loop_block.expr.is_none() && !maybe_emitted_in_if { + check_last_stmt_in_block(loop_block, &p); + } + } + + if let Some(expr) = loop_block.expr { + let mut maybe_emitted_in_if = false; + + with_if_expr(expr, |if_expr, cond, then_block, else_expr| { let data = &LintData { if_expr, if_cond: cond, if_block: then_block, else_expr, - stmt_idx: i, + stmt_idx: None, loop_block, }; @@ -455,7 +533,7 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { } }); - if i == stmts.len() - 1 && !maybe_emitted_in_if { + if !maybe_emitted_in_if { check_last_stmt_in_block(loop_block, &p); } } @@ -491,8 +569,12 @@ fn erode_from_back(s: &str) -> String { if ret.is_empty() { s.to_string() } else { ret } } -fn span_of_first_expr_in_block(block: &Block) -> Option { - block.stmts.first().map(|stmt| stmt.span) +fn span_of_first_expr_in_block(block: &Block<'_>) -> Option { + block + .stmts + .first() + .map(|stmt| stmt.span) + .or(block.expr.map(|expr| expr.span)) } #[cfg(test)] diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 95e6ecd40dec6..9fe6d8c62d2a5 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -14,6 +14,7 @@ use rustc_span::{Span, symbol}; /// The essential nodes of a desugared for loop as well as the entire span: /// `for pat in arg { body }` becomes `(pat, arg, body)`. Returns `(pat, arg, body, span)`. +#[derive(Debug)] pub struct ForLoop<'tcx> { /// `for` loop item pub pat: &'tcx Pat<'tcx>, @@ -318,6 +319,7 @@ pub struct While<'hir> { pub body: &'hir Expr<'hir>, /// Span of the loop header pub span: Span, + pub label: Option, } impl<'hir> While<'hir> { @@ -333,13 +335,18 @@ impl<'hir> While<'hir> { }), .. }, - _, + label, LoopSource::While, span, ) = expr.kind && !has_let_expr(condition) { - return Some(Self { condition, body, span }); + return Some(Self { + condition, + body, + span, + label, + }); } None } From 7117bd9a271a43e5fb3c2c25b4a27feac3017eb7 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Sun, 24 Aug 2025 02:48:30 +0800 Subject: [PATCH 20/89] fix: `needless_continue` FP when match type is not unit or never --- clippy_lints/src/needless_continue.rs | 24 ++++++++++++++---------- tests/ui/needless_continue.rs | 15 +++++++++++++++ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index 2097038e1fde8..4bdac7cfd0777 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -413,7 +413,7 @@ fn suggestion_snippet_for_continue_inside_else(cx: &LateContext<'_>, data: &Lint ) } -fn check_last_stmt_in_expr(inner_expr: &Expr<'_>, func: &F) +fn check_last_stmt_in_expr(cx: &LateContext<'_>, inner_expr: &Expr<'_>, func: &F) where F: Fn(Option<&Label>, Span), { @@ -422,36 +422,40 @@ where func(continue_label.label.as_ref(), inner_expr.span); }, ExprKind::If(_, then_block, else_block) if let ExprKind::Block(then_block, _) = then_block.kind => { - check_last_stmt_in_block(then_block, func); + check_last_stmt_in_block(cx, then_block, func); if let Some(else_block) = else_block { - check_last_stmt_in_expr(else_block, func); + check_last_stmt_in_expr(cx, else_block, func); } }, ExprKind::Match(_, arms, _) => { + let match_ty = cx.typeck_results().expr_ty(inner_expr); + if !match_ty.is_unit() && !match_ty.is_never() { + return; + } for arm in arms.iter() { - check_last_stmt_in_expr(arm.body, func); + check_last_stmt_in_expr(cx, arm.body, func); } }, ExprKind::Block(b, _) => { - check_last_stmt_in_block(b, func); + check_last_stmt_in_block(cx, b, func); }, _ => {}, } } -fn check_last_stmt_in_block(b: &Block<'_>, func: &F) +fn check_last_stmt_in_block(cx: &LateContext<'_>, b: &Block<'_>, func: &F) where F: Fn(Option<&Label>, Span), { if let Some(expr) = b.expr { - check_last_stmt_in_expr(expr, func); + check_last_stmt_in_expr(cx, expr, func); return; } if let Some(last_stmt) = b.stmts.last() && let StmtKind::Expr(inner_expr) | StmtKind::Semi(inner_expr) = last_stmt.kind { - check_last_stmt_in_expr(inner_expr, func); + check_last_stmt_in_expr(cx, inner_expr, func); } } @@ -501,7 +505,7 @@ fn check_and_warn(cx: &LateContext<'_>, expr: &Expr<'_>) { } if i == stmts.len() - 1 && loop_block.expr.is_none() && !maybe_emitted_in_if { - check_last_stmt_in_block(loop_block, &p); + check_last_stmt_in_block(cx, loop_block, &p); } } @@ -534,7 +538,7 @@ fn check_and_warn(cx: &LateContext<'_>, expr: &Expr<'_>) { }); if !maybe_emitted_in_if { - check_last_stmt_in_block(loop_block, &p); + check_last_stmt_in_block(cx, loop_block, &p); } } }); diff --git a/tests/ui/needless_continue.rs b/tests/ui/needless_continue.rs index e873db6dee14e..003ca64aa9bea 100644 --- a/tests/ui/needless_continue.rs +++ b/tests/ui/needless_continue.rs @@ -244,3 +244,18 @@ mod issue_4077 { true } } + +#[allow(clippy::let_unit_value)] +fn issue14550(mut producer: impl Iterator>) -> Result { + let mut counter = 2; + loop { + match producer.next().unwrap() { + Ok(ok) => break Ok((ok + 1) as u32), + Err(12) => { + counter -= 1; + continue; + }, + err => err?, + }; + } +} From 13bd9b5d0f37c7bea5657692ce50664fd28289b1 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Sun, 24 Aug 2025 03:04:23 +0800 Subject: [PATCH 21/89] fix: `needless_continue` wrongly unmangled macros --- clippy_lints/src/needless_continue.rs | 53 ++++++++------ tests/ui/needless_continue.rs | 101 +++++++++++++++++++++++--- tests/ui/needless_continue.stderr | 18 ++++- 3 files changed, 140 insertions(+), 32 deletions(-) diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index 4bdac7cfd0777..55208ae708b93 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::source::{indent_of, snippet, snippet_block, snippet_with_context}; -use clippy_utils::{higher, is_from_proc_macro}; +use clippy_utils::higher; +use clippy_utils::source::{indent_of, snippet_block, snippet_with_context}; use rustc_ast::Label; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, LoopSource, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; -use rustc_span::Span; +use rustc_span::{ExpnKind, Span}; declare_clippy_lint! { /// ### What it does @@ -132,7 +132,9 @@ declare_lint_pass!(NeedlessContinue => [NEEDLESS_CONTINUE]); impl<'tcx> LateLintPass<'tcx> for NeedlessContinue { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !expr.span.in_external_macro(cx.sess().source_map()) && !is_from_proc_macro(cx, expr) { + // We cannot use `from_expansion` because for loops, while loops and while let loops are desugared + // into `loop` expressions. + if !matches!(expr.span.ctxt().outer_expn_data().kind, ExpnKind::Macro(..)) { check_and_warn(cx, expr); } } @@ -193,7 +195,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessContinue { /// - The expression node is a block with the first statement being a `continue`. fn needless_continue_in_else(else_expr: &Expr<'_>, label: Option<&Label>) -> bool { match else_expr.kind { - ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label), + ExprKind::Block(else_block, _) => is_first_block_stmt_continue(else_block, label), ExprKind::Continue(l) => compare_labels(label, l.label.as_ref()), _ => false, } @@ -201,8 +203,8 @@ fn needless_continue_in_else(else_expr: &Expr<'_>, label: Option<&Label>) -> boo fn is_first_block_stmt_continue(block: &Block<'_>, label: Option<&Label>) -> bool { block.stmts.first().is_some_and(|stmt| match stmt.kind { - StmtKind::Semi(ref e) | StmtKind::Expr(ref e) => { - if let ExprKind::Continue(ref l) = e.kind { + StmtKind::Semi(e) | StmtKind::Expr(e) => { + if let ExprKind::Continue(l) = e.kind { compare_labels(label, l.label.as_ref()) } else { false @@ -225,10 +227,10 @@ fn compare_labels(loop_label: Option<&Label>, continue_label: Option<&Label>) -> } /// If `expr` is a loop expression (while/while let/for/loop), calls `func` with -/// the AST object representing the loop block of `expr`. -fn with_loop_block<'tcx, F>(expr: &Expr<'tcx>, mut func: F) +/// the HIR object representing the loop block of `expr`. +fn with_loop_block(expr: &Expr<'_>, mut func: F) where - F: FnMut(&Block<'tcx>, Option<&Label>), + F: FnMut(&Block<'_>, Option<&Label>), { if let Some(higher::ForLoop { body, label, .. }) = higher::ForLoop::hir(expr) && let ExprKind::Block(block, _) = &body.kind @@ -264,9 +266,9 @@ where /// - The `if` condition expression, /// - The `then` block, and /// - The `else` expression. -fn with_if_expr<'tcx, F>(expr: &Expr<'tcx>, mut func: F) +fn with_if_expr(expr: &Expr<'_>, mut func: F) where - F: FnMut(&Expr<'tcx>, &Expr<'tcx>, &Block<'tcx>, &Expr<'tcx>), + F: FnMut(&Expr<'_>, &Expr<'_>, &Block<'_>, &Expr<'_>), { if let Some(higher::If { cond, @@ -288,20 +290,20 @@ enum LintType { /// Data we pass around for construction of help messages. #[derive(Debug)] -struct LintData<'tcx> { +struct LintData<'hir> { /// The `if` expression encountered in the above loop. - if_expr: &'tcx Expr<'tcx>, + if_expr: &'hir Expr<'hir>, /// The condition expression for the above `if`. - if_cond: &'tcx Expr<'tcx>, + if_cond: &'hir Expr<'hir>, /// The `then` block of the `if` statement. - if_block: &'tcx Block<'tcx>, + if_block: &'hir Block<'hir>, /// The `else` block of the `if` statement. /// Note that we only work with `if` exprs that have an `else` branch. - else_expr: &'tcx Expr<'tcx>, + else_expr: &'hir Expr<'hir>, /// The 0-based index of the `if` statement in the containing loop block. stmt_idx: Option, /// The statements of the loop block. - loop_block: &'tcx Block<'tcx>, + loop_block: &'hir Block<'hir>, } const MSG_REDUNDANT_CONTINUE_EXPRESSION: &str = "this `continue` expression is redundant"; @@ -366,7 +368,14 @@ fn suggestion_snippet_for_continue_inside_if(cx: &LateContext<'_>, data: &LintDa } fn suggestion_snippet_for_continue_inside_else(cx: &LateContext<'_>, data: &LintData<'_>) -> String { - let cond_code = snippet(cx, data.if_cond.span, ".."); + let mut applicability = Applicability::MachineApplicable; + let (cond_code, _) = snippet_with_context( + cx, + data.if_cond.span, + data.if_expr.span.ctxt(), + "..", + &mut applicability, + ); // Region B let block_code = erode_from_back(&snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span))); @@ -402,7 +411,7 @@ fn suggestion_snippet_for_continue_inside_else(cx: &LateContext<'_>, data: &Lint } lines.join("\n") } else { - "".to_string() + String::new() }; let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0); @@ -417,7 +426,7 @@ fn check_last_stmt_in_expr(cx: &LateContext<'_>, inner_expr: &Expr<'_>, func: where F: Fn(Option<&Label>, Span), { - match &inner_expr.kind { + match inner_expr.kind { ExprKind::Continue(continue_label) => { func(continue_label.label.as_ref(), inner_expr.span); }, @@ -432,7 +441,7 @@ where if !match_ty.is_unit() && !match_ty.is_never() { return; } - for arm in arms.iter() { + for arm in arms { check_last_stmt_in_expr(cx, arm.body, func); } }, diff --git a/tests/ui/needless_continue.rs b/tests/ui/needless_continue.rs index 003ca64aa9bea..88b7905e7fa80 100644 --- a/tests/ui/needless_continue.rs +++ b/tests/ui/needless_continue.rs @@ -246,16 +246,99 @@ mod issue_4077 { } #[allow(clippy::let_unit_value)] -fn issue14550(mut producer: impl Iterator>) -> Result { - let mut counter = 2; - loop { - match producer.next().unwrap() { - Ok(ok) => break Ok((ok + 1) as u32), - Err(12) => { - counter -= 1; +mod issue14550 { + fn match_with_value(mut producer: impl Iterator>) -> Result { + let mut counter = 2; + loop { + match producer.next().unwrap() { + Ok(ok) => break Ok((ok + 1) as u32), + Err(12) => { + counter -= 1; + continue; + }, + err => err?, + }; + } + } + + fn inside_macro() { + macro_rules! mac { + ($e:expr => $($rest:tt);*) => { + loop { + match $e { + 1 => continue, + 2 => break, + n => println!("{n}"), + } + $($rest;)* + } + }; + } + + mac!(2 => ); + mac!(1 => {println!("foobar")}); + } + + mod partially_inside_macro { + macro_rules! select { + ( + $expr:expr, + $( $pat:pat => $then:expr ),* + ) => { + fn foo() { + loop { + match $expr { + $( + $pat => $then, + )* + } + } + } + }; + } + + select!(Some(1), + Some(1) => { + println!("one"); continue; }, - err => err?, - }; + Some(2) => {}, + None => break, + _ => () + ); + + macro_rules! choose { + ( + $expr:expr, + $case:expr + ) => { + fn bar() { + loop { + match $expr { + $case => { + println!("matched"); + continue; + }, + _ => { + println!("not matched"); + break; + }, + } + } + } + }; + } + + choose!(todo!(), 5); + } +} + +fn issue15548() { + loop { + if todo!() { + } else { + //~^ needless_continue + continue; + } } } diff --git a/tests/ui/needless_continue.stderr b/tests/ui/needless_continue.stderr index 878c1e731e32a..7a65872c85cd2 100644 --- a/tests/ui/needless_continue.stderr +++ b/tests/ui/needless_continue.stderr @@ -220,5 +220,21 @@ LL | | } do_something(); } -error: aborting due to 15 previous errors +error: this `else` block is redundant + --> tests/ui/needless_continue.rs:339:16 + | +LL | } else { + | ________________^ +LL | | +LL | | continue; +LL | | } + | |_________^ + | + = help: consider dropping the `else` clause and merging the code that follows (in the loop) with the `if` block + if todo!() { + // merged code follows: + + } + +error: aborting due to 16 previous errors From e5e3bbd9a1fdf989023da5e8d37190beb987662a Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 7 Oct 2025 18:23:58 +0200 Subject: [PATCH 22/89] extend the let-chain --- clippy_lints/src/replace_box.rs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/replace_box.rs b/clippy_lints/src/replace_box.rs index 9388f77a839d9..457c9a52a8574 100644 --- a/clippy_lints/src/replace_box.rs +++ b/clippy_lints/src/replace_box.rs @@ -40,20 +40,11 @@ impl LateLintPass<'_> for ReplaceBox { if let ExprKind::Assign(lhs, rhs, _) = &expr.kind && !lhs.span.from_expansion() && !rhs.span.from_expansion() - { - let lhs_ty = cx.typeck_results().expr_ty(lhs); - + && let lhs_ty = cx.typeck_results().expr_ty(lhs) // No diagnostic for late-initialized locals - if let Some(local) = path_to_local(lhs) - && !local_is_initialized(cx, local) - { - return; - } - - let Some(inner_ty) = get_box_inner_type(cx, lhs_ty) else { - return; - }; - + && path_to_local(lhs).is_none_or(|local| local_is_initialized(cx, local)) + && let Some(inner_ty) = get_box_inner_type(cx, lhs_ty) + { if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) && implements_trait(cx, inner_ty, default_trait_id, &[]) && is_default_call(cx, rhs) From 01d2adc2f994c23e920044ea1744867734f2661d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 7 Oct 2025 18:25:56 +0200 Subject: [PATCH 23/89] replace `get_box_inner_type` with `Ty::boxed_ty` --- clippy_lints/src/replace_box.rs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/replace_box.rs b/clippy_lints/src/replace_box.rs index 457c9a52a8574..9ad61f25dfcc4 100644 --- a/clippy_lints/src/replace_box.rs +++ b/clippy_lints/src/replace_box.rs @@ -3,9 +3,8 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::{is_default_equivalent_call, local_is_initialized, path_def_id, path_to_local}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, QPath}; +use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -43,7 +42,7 @@ impl LateLintPass<'_> for ReplaceBox { && let lhs_ty = cx.typeck_results().expr_ty(lhs) // No diagnostic for late-initialized locals && path_to_local(lhs).is_none_or(|local| local_is_initialized(cx, local)) - && let Some(inner_ty) = get_box_inner_type(cx, lhs_ty) + && let Some(inner_ty) = lhs_ty.boxed_ty() { if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) && implements_trait(cx, inner_ty, default_trait_id, &[]) @@ -94,16 +93,6 @@ impl LateLintPass<'_> for ReplaceBox { } } -fn get_box_inner_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - if let ty::Adt(def, args) = ty.kind() - && cx.tcx.is_lang_item(def.did(), LangItem::OwnedBox) - { - Some(args.type_at(0)) - } else { - None - } -} - fn is_default_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { matches!(expr.kind, ExprKind::Call(func, _args) if is_default_equivalent_call(cx, func, Some(expr))) } From f6336bc8e425d806f913494a2c7897e8b3ed47ec Mon Sep 17 00:00:00 2001 From: Boxy Uwu Date: Sun, 28 Sep 2025 00:22:36 +0100 Subject: [PATCH 24/89] rename `select_where_possible` and `select_all_or_error` --- clippy_lints/src/future_not_send.rs | 2 +- clippy_utils/src/qualify_min_const_fn.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 596047977a9b1..221107ba4b93e 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let cause = traits::ObligationCause::misc(span, fn_def_id); ocx.register_bound(cause, cx.param_env, ret_ty, send_trait); - let send_errors = ocx.select_all_or_error(); + let send_errors = ocx.evaluate_obligations_error_on_ambiguity(); // Allow errors that try to prove `Send` for types that "mention" a generic parameter at the "top // level". diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index cc98fac45c7cb..1e3a7281bc734 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -475,7 +475,7 @@ fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx> let ocx = ObligationCtxt::new(&infcx); ocx.register_obligations(impl_src.nested_obligations()); - ocx.select_all_or_error().is_empty() + ocx.evaluate_obligations_error_on_ambiguity().is_empty() } !ty.needs_drop(tcx, ConstCx::new(tcx, body).typing_env) From 8ef90574a270563f545675668888ca93c3dcc921 Mon Sep 17 00:00:00 2001 From: Scott Gerring Date: Wed, 8 Oct 2025 11:58:50 +0100 Subject: [PATCH 25/89] chore: update manual_assert span suggestions --- clippy_lints/src/manual_assert.rs | 37 +++---- tests/ui/manual_assert.edition2018.fixed | 98 +++++++++++++++++++ tests/ui/manual_assert.edition2018.stderr | 114 ++++++++-------------- tests/ui/manual_assert.edition2021.fixed | 98 +++++++++++++++++++ tests/ui/manual_assert.edition2021.stderr | 114 ++++++++-------------- tests/ui/manual_assert.rs | 2 - 6 files changed, 297 insertions(+), 166 deletions(-) create mode 100644 tests/ui/manual_assert.edition2018.fixed create mode 100644 tests/ui/manual_assert.edition2021.fixed diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index 76cb22864779f..c34e0d33e713e 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_panic, root_macro_call}; +use clippy_utils::source::{indent_of, reindent_multiline}; use clippy_utils::{higher, is_else_clause, is_parent_stmt, peel_blocks_with_stmt, span_extract_comment, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -50,32 +51,32 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert { // Should this have a config value? && !is_else_clause(cx.tcx, expr) { - let mut applicability = Applicability::MachineApplicable; - let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); - if !comments.is_empty() { - comments += "\n"; - } - let cond_sugg = !sugg::Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "..", &mut applicability); - let semicolon = if is_parent_stmt(cx, expr.hir_id) { ";" } else { "" }; - let sugg = format!("assert!({cond_sugg}, {format_args_snip}){semicolon}"); - // we show to the user the suggestion without the comments, but when applying the fix, include the - // comments in the block span_lint_and_then( cx, MANUAL_ASSERT, expr.span, "only a `panic!` in `if`-then statement", |diag| { - // comments can be noisy, do not show them to the user + let mut applicability = Applicability::MachineApplicable; + let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); if !comments.is_empty() { - diag.tool_only_span_suggestion( - expr.span.shrink_to_lo(), - "add comments back", - comments, - applicability, - ); + comments += "\n"; } - diag.span_suggestion(expr.span, "try instead", sugg, applicability); + let cond_sugg = !sugg::Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "..", &mut applicability); + let semicolon = if is_parent_stmt(cx, expr.hir_id) { ";" } else { "" }; + + let indent = indent_of(cx, expr.span); + let full_sugg = reindent_multiline( + format!("{comments}assert!({cond_sugg}, {format_args_snip}){semicolon}").as_str(), + true, + indent, + ); + diag.span_suggestion_verbose( + expr.span, + "replace `if`-then-`panic!` with `assert!`", + full_sugg, + applicability, + ); }, ); } diff --git a/tests/ui/manual_assert.edition2018.fixed b/tests/ui/manual_assert.edition2018.fixed new file mode 100644 index 0000000000000..d1f798bd5c54b --- /dev/null +++ b/tests/ui/manual_assert.edition2018.fixed @@ -0,0 +1,98 @@ +//@revisions: edition2018 edition2021 +//@[edition2018] edition:2018 +//@[edition2021] edition:2021 + +#![warn(clippy::manual_assert)] +#![allow(dead_code, unused_doc_comments)] +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)] + +macro_rules! one { + () => { + 1 + }; +} + +fn main() { + let a = vec![1, 2, 3]; + let c = Some(2); + if !a.is_empty() + && a.len() == 3 + && c.is_some() + && !a.is_empty() + && a.len() == 3 + && !a.is_empty() + && a.len() == 3 + && !a.is_empty() + && a.len() == 3 + { + panic!("qaqaq{:?}", a); + } + //~^ manual_assert + assert!(a.is_empty(), "qaqaq{:?}", a); + //~^ manual_assert + assert!(a.is_empty(), "qwqwq"); + if a.len() == 3 { + println!("qwq"); + println!("qwq"); + println!("qwq"); + } + if let Some(b) = c { + panic!("orz {}", b); + } + if a.len() == 3 { + panic!("qaqaq"); + } else { + println!("qwq"); + } + let b = vec![1, 2, 3]; + //~^ manual_assert + assert!(!b.is_empty(), "panic1"); + //~^ manual_assert + assert!(!(b.is_empty() && a.is_empty()), "panic2"); + //~^ manual_assert + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + //~^ manual_assert + assert!(!(b.is_empty() || a.is_empty()), "panic4"); + //~^ manual_assert + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); + //~^ manual_assert + assert!(!a.is_empty(), "with expansion {}", one!()); + if a.is_empty() { + let _ = 0; + } else if a.len() == 1 { + panic!("panic6"); + } +} + +fn issue7730(a: u8) { + // Suggestion should preserve comment + //~^ manual_assert + // comment + /* this is a + multiline + comment */ + /// Doc comment + // comment after `panic!` + assert!(a <= 2, "panic with comment"); +} + +fn issue12505() { + struct Foo(T); + + impl Foo { + const BAR: () = //~^ manual_assert + assert!(N != 0, ); + } +} + +fn issue15227(left: u64, right: u64) -> u64 { + macro_rules! is_x86_feature_detected { + ($feature:literal) => { + $feature.len() > 0 && $feature.starts_with("ss") + }; + } + + //~^ manual_assert + assert!(is_x86_feature_detected!("ssse3"), "SSSE3 is not supported"); + unsafe { todo!() } +} diff --git a/tests/ui/manual_assert.edition2018.stderr b/tests/ui/manual_assert.edition2018.stderr index 2e9c9045caae1..c81a855275270 100644 --- a/tests/ui/manual_assert.edition2018.stderr +++ b/tests/ui/manual_assert.edition2018.stderr @@ -1,5 +1,5 @@ error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:32:5 + --> tests/ui/manual_assert.rs:30:5 | LL | / if !a.is_empty() { LL | | @@ -9,17 +9,14 @@ LL | | } | = note: `-D clippy::manual-assert` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_assert)]` -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !a.is_empty() { -LL - -LL - panic!("qaqaq{:?}", a); -LL - } +LL ~ LL + assert!(a.is_empty(), "qaqaq{:?}", a); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:36:5 + --> tests/ui/manual_assert.rs:34:5 | LL | / if !a.is_empty() { LL | | @@ -27,17 +24,14 @@ LL | | panic!("qwqwq"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !a.is_empty() { -LL - -LL - panic!("qwqwq"); -LL - } +LL ~ LL + assert!(a.is_empty(), "qwqwq"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:54:5 + --> tests/ui/manual_assert.rs:52:5 | LL | / if b.is_empty() { LL | | @@ -45,17 +39,14 @@ LL | | panic!("panic1"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() { -LL - -LL - panic!("panic1"); -LL - } +LL ~ LL + assert!(!b.is_empty(), "panic1"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:58:5 + --> tests/ui/manual_assert.rs:56:5 | LL | / if b.is_empty() && a.is_empty() { LL | | @@ -63,17 +54,14 @@ LL | | panic!("panic2"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() && a.is_empty() { -LL - -LL - panic!("panic2"); -LL - } +LL ~ LL + assert!(!(b.is_empty() && a.is_empty()), "panic2"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:62:5 + --> tests/ui/manual_assert.rs:60:5 | LL | / if a.is_empty() && !b.is_empty() { LL | | @@ -81,17 +69,14 @@ LL | | panic!("panic3"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() && !b.is_empty() { -LL - -LL - panic!("panic3"); -LL - } +LL ~ LL + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:66:5 + --> tests/ui/manual_assert.rs:64:5 | LL | / if b.is_empty() || a.is_empty() { LL | | @@ -99,17 +84,14 @@ LL | | panic!("panic4"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() || a.is_empty() { -LL - -LL - panic!("panic4"); -LL - } +LL ~ LL + assert!(!(b.is_empty() || a.is_empty()), "panic4"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:70:5 + --> tests/ui/manual_assert.rs:68:5 | LL | / if a.is_empty() || !b.is_empty() { LL | | @@ -117,17 +99,14 @@ LL | | panic!("panic5"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() || !b.is_empty() { -LL - -LL - panic!("panic5"); -LL - } +LL ~ LL + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:74:5 + --> tests/ui/manual_assert.rs:72:5 | LL | / if a.is_empty() { LL | | @@ -135,17 +114,14 @@ LL | | panic!("with expansion {}", one!()) LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() { -LL - -LL - panic!("with expansion {}", one!()) -LL - } +LL ~ LL + assert!(!a.is_empty(), "with expansion {}", one!()); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:87:5 + --> tests/ui/manual_assert.rs:85:5 | LL | / if a > 2 { LL | | @@ -156,22 +132,20 @@ LL | | panic!("panic with comment") // comment after `panic!` LL | | } | |_____^ | -help: try instead - | -LL - if a > 2 { -LL - -LL - // comment -LL - /* this is a -LL - multiline -LL - comment */ -LL - /// Doc comment -LL - panic!("panic with comment") // comment after `panic!` -LL - } +help: replace `if`-then-`panic!` with `assert!` + | +LL ~ +LL + // comment +LL + /* this is a +LL + multiline +LL + comment */ +LL + /// Doc comment +LL + // comment after `panic!` LL + assert!(a <= 2, "panic with comment"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:102:25 + --> tests/ui/manual_assert.rs:100:25 | LL | const BAR: () = if N == 0 { | _________________________^ @@ -180,17 +154,14 @@ LL | | panic!() LL | | }; | |_________^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - const BAR: () = if N == 0 { -LL - -LL - panic!() -LL - }; -LL + const BAR: () = assert!(N != 0, ); +LL ~ const BAR: () = +LL ~ assert!(N != 0, ); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:116:5 + --> tests/ui/manual_assert.rs:114:5 | LL | / if !is_x86_feature_detected!("ssse3") { LL | | @@ -198,12 +169,9 @@ LL | | panic!("SSSE3 is not supported"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !is_x86_feature_detected!("ssse3") { -LL - -LL - panic!("SSSE3 is not supported"); -LL - } +LL ~ LL + assert!(is_x86_feature_detected!("ssse3"), "SSSE3 is not supported"); | diff --git a/tests/ui/manual_assert.edition2021.fixed b/tests/ui/manual_assert.edition2021.fixed new file mode 100644 index 0000000000000..d1f798bd5c54b --- /dev/null +++ b/tests/ui/manual_assert.edition2021.fixed @@ -0,0 +1,98 @@ +//@revisions: edition2018 edition2021 +//@[edition2018] edition:2018 +//@[edition2021] edition:2021 + +#![warn(clippy::manual_assert)] +#![allow(dead_code, unused_doc_comments)] +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)] + +macro_rules! one { + () => { + 1 + }; +} + +fn main() { + let a = vec![1, 2, 3]; + let c = Some(2); + if !a.is_empty() + && a.len() == 3 + && c.is_some() + && !a.is_empty() + && a.len() == 3 + && !a.is_empty() + && a.len() == 3 + && !a.is_empty() + && a.len() == 3 + { + panic!("qaqaq{:?}", a); + } + //~^ manual_assert + assert!(a.is_empty(), "qaqaq{:?}", a); + //~^ manual_assert + assert!(a.is_empty(), "qwqwq"); + if a.len() == 3 { + println!("qwq"); + println!("qwq"); + println!("qwq"); + } + if let Some(b) = c { + panic!("orz {}", b); + } + if a.len() == 3 { + panic!("qaqaq"); + } else { + println!("qwq"); + } + let b = vec![1, 2, 3]; + //~^ manual_assert + assert!(!b.is_empty(), "panic1"); + //~^ manual_assert + assert!(!(b.is_empty() && a.is_empty()), "panic2"); + //~^ manual_assert + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + //~^ manual_assert + assert!(!(b.is_empty() || a.is_empty()), "panic4"); + //~^ manual_assert + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); + //~^ manual_assert + assert!(!a.is_empty(), "with expansion {}", one!()); + if a.is_empty() { + let _ = 0; + } else if a.len() == 1 { + panic!("panic6"); + } +} + +fn issue7730(a: u8) { + // Suggestion should preserve comment + //~^ manual_assert + // comment + /* this is a + multiline + comment */ + /// Doc comment + // comment after `panic!` + assert!(a <= 2, "panic with comment"); +} + +fn issue12505() { + struct Foo(T); + + impl Foo { + const BAR: () = //~^ manual_assert + assert!(N != 0, ); + } +} + +fn issue15227(left: u64, right: u64) -> u64 { + macro_rules! is_x86_feature_detected { + ($feature:literal) => { + $feature.len() > 0 && $feature.starts_with("ss") + }; + } + + //~^ manual_assert + assert!(is_x86_feature_detected!("ssse3"), "SSSE3 is not supported"); + unsafe { todo!() } +} diff --git a/tests/ui/manual_assert.edition2021.stderr b/tests/ui/manual_assert.edition2021.stderr index 2e9c9045caae1..c81a855275270 100644 --- a/tests/ui/manual_assert.edition2021.stderr +++ b/tests/ui/manual_assert.edition2021.stderr @@ -1,5 +1,5 @@ error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:32:5 + --> tests/ui/manual_assert.rs:30:5 | LL | / if !a.is_empty() { LL | | @@ -9,17 +9,14 @@ LL | | } | = note: `-D clippy::manual-assert` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_assert)]` -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !a.is_empty() { -LL - -LL - panic!("qaqaq{:?}", a); -LL - } +LL ~ LL + assert!(a.is_empty(), "qaqaq{:?}", a); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:36:5 + --> tests/ui/manual_assert.rs:34:5 | LL | / if !a.is_empty() { LL | | @@ -27,17 +24,14 @@ LL | | panic!("qwqwq"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !a.is_empty() { -LL - -LL - panic!("qwqwq"); -LL - } +LL ~ LL + assert!(a.is_empty(), "qwqwq"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:54:5 + --> tests/ui/manual_assert.rs:52:5 | LL | / if b.is_empty() { LL | | @@ -45,17 +39,14 @@ LL | | panic!("panic1"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() { -LL - -LL - panic!("panic1"); -LL - } +LL ~ LL + assert!(!b.is_empty(), "panic1"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:58:5 + --> tests/ui/manual_assert.rs:56:5 | LL | / if b.is_empty() && a.is_empty() { LL | | @@ -63,17 +54,14 @@ LL | | panic!("panic2"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() && a.is_empty() { -LL - -LL - panic!("panic2"); -LL - } +LL ~ LL + assert!(!(b.is_empty() && a.is_empty()), "panic2"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:62:5 + --> tests/ui/manual_assert.rs:60:5 | LL | / if a.is_empty() && !b.is_empty() { LL | | @@ -81,17 +69,14 @@ LL | | panic!("panic3"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() && !b.is_empty() { -LL - -LL - panic!("panic3"); -LL - } +LL ~ LL + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:66:5 + --> tests/ui/manual_assert.rs:64:5 | LL | / if b.is_empty() || a.is_empty() { LL | | @@ -99,17 +84,14 @@ LL | | panic!("panic4"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() || a.is_empty() { -LL - -LL - panic!("panic4"); -LL - } +LL ~ LL + assert!(!(b.is_empty() || a.is_empty()), "panic4"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:70:5 + --> tests/ui/manual_assert.rs:68:5 | LL | / if a.is_empty() || !b.is_empty() { LL | | @@ -117,17 +99,14 @@ LL | | panic!("panic5"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() || !b.is_empty() { -LL - -LL - panic!("panic5"); -LL - } +LL ~ LL + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:74:5 + --> tests/ui/manual_assert.rs:72:5 | LL | / if a.is_empty() { LL | | @@ -135,17 +114,14 @@ LL | | panic!("with expansion {}", one!()) LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() { -LL - -LL - panic!("with expansion {}", one!()) -LL - } +LL ~ LL + assert!(!a.is_empty(), "with expansion {}", one!()); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:87:5 + --> tests/ui/manual_assert.rs:85:5 | LL | / if a > 2 { LL | | @@ -156,22 +132,20 @@ LL | | panic!("panic with comment") // comment after `panic!` LL | | } | |_____^ | -help: try instead - | -LL - if a > 2 { -LL - -LL - // comment -LL - /* this is a -LL - multiline -LL - comment */ -LL - /// Doc comment -LL - panic!("panic with comment") // comment after `panic!` -LL - } +help: replace `if`-then-`panic!` with `assert!` + | +LL ~ +LL + // comment +LL + /* this is a +LL + multiline +LL + comment */ +LL + /// Doc comment +LL + // comment after `panic!` LL + assert!(a <= 2, "panic with comment"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:102:25 + --> tests/ui/manual_assert.rs:100:25 | LL | const BAR: () = if N == 0 { | _________________________^ @@ -180,17 +154,14 @@ LL | | panic!() LL | | }; | |_________^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - const BAR: () = if N == 0 { -LL - -LL - panic!() -LL - }; -LL + const BAR: () = assert!(N != 0, ); +LL ~ const BAR: () = +LL ~ assert!(N != 0, ); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:116:5 + --> tests/ui/manual_assert.rs:114:5 | LL | / if !is_x86_feature_detected!("ssse3") { LL | | @@ -198,12 +169,9 @@ LL | | panic!("SSSE3 is not supported"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !is_x86_feature_detected!("ssse3") { -LL - -LL - panic!("SSSE3 is not supported"); -LL - } +LL ~ LL + assert!(is_x86_feature_detected!("ssse3"), "SSSE3 is not supported"); | diff --git a/tests/ui/manual_assert.rs b/tests/ui/manual_assert.rs index ab02bd5f5e533..7611795542230 100644 --- a/tests/ui/manual_assert.rs +++ b/tests/ui/manual_assert.rs @@ -2,8 +2,6 @@ //@[edition2018] edition:2018 //@[edition2021] edition:2021 -//@no-rustfix: need to change the suggestion to a multipart suggestion - #![warn(clippy::manual_assert)] #![allow(dead_code, unused_doc_comments)] #![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)] From d1be6d810b666fdf92adbe75c42d2c058201de46 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 8 Oct 2025 18:10:31 +0200 Subject: [PATCH 26/89] Make `obfuscated_if_else` a bit more type-safe --- clippy_lints/src/methods/mod.rs | 15 ++++-- .../src/methods/obfuscated_if_else.rs | 51 ++++++++++++------- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a8f1ec7e91986..940e0c51c7d1e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5501,7 +5501,14 @@ impl Methods { option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, self.msrv); }, Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => { - obfuscated_if_else::check(cx, expr, t_recv, t_arg, Some(u_arg), then_method, name); + obfuscated_if_else::check( + cx, + expr, + t_recv, + t_arg, + then_method, + obfuscated_if_else::Unwrap::Or(u_arg), + ); }, _ => {}, } @@ -5518,9 +5525,8 @@ impl Methods { expr, t_recv, t_arg, - None, then_method, - sym::unwrap_or_default, + obfuscated_if_else::Unwrap::OrDefault, ); }, _ => {}, @@ -5537,9 +5543,8 @@ impl Methods { expr, t_recv, t_arg, - Some(u_arg), then_method, - sym::unwrap_or_else, + obfuscated_if_else::Unwrap::OrElse(u_arg), ); }, _ => { diff --git a/clippy_lints/src/methods/obfuscated_if_else.rs b/clippy_lints/src/methods/obfuscated_if_else.rs index 604b48656aeac..b2466bbd982d9 100644 --- a/clippy_lints/src/methods/obfuscated_if_else.rs +++ b/clippy_lints/src/methods/obfuscated_if_else.rs @@ -5,25 +5,24 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{get_parent_expr, sym}; use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::ExprKind; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_span::Symbol; +#[expect(clippy::needless_pass_by_value)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - then_recv: &'tcx hir::Expr<'_>, - then_arg: &'tcx hir::Expr<'_>, - unwrap_arg: Option<&'tcx hir::Expr<'_>>, + expr: &'tcx Expr<'_>, + then_recv: &'tcx Expr<'_>, + then_arg: &'tcx Expr<'_>, then_method_name: Symbol, - unwrap_method_name: Symbol, + unwrap: Unwrap<'tcx>, ) { let recv_ty = cx.typeck_results().expr_ty(then_recv); if recv_ty.is_bool() { let then_eager = switch_to_eager_eval(cx, then_arg); - let unwrap_eager = unwrap_arg.is_none_or(|arg| switch_to_eager_eval(cx, arg)); + let unwrap_eager = unwrap.arg().is_none_or(|arg| switch_to_eager_eval(cx, arg)); let mut applicability = if then_eager && unwrap_eager { Applicability::MachineApplicable @@ -40,18 +39,17 @@ pub(super) fn check<'tcx>( _ => return, }; - // FIXME: Add `unwrap_or_else` and `unwrap_or_default` symbol - let els = match unwrap_method_name { - sym::unwrap_or => snippet_with_applicability(cx, unwrap_arg.unwrap().span, "..", &mut applicability), - sym::unwrap_or_else if let ExprKind::Closure(closure) = unwrap_arg.unwrap().kind => { - let body = cx.tcx.hir_body(closure.body); - snippet_with_applicability(cx, body.value.span, "..", &mut applicability) - }, - sym::unwrap_or_else if let ExprKind::Path(_) = unwrap_arg.unwrap().kind => { - snippet_with_applicability(cx, unwrap_arg.unwrap().span, "_", &mut applicability) + "()" + let els = match unwrap { + Unwrap::Or(arg) => snippet_with_applicability(cx, arg.span, "..", &mut applicability), + Unwrap::OrElse(arg) => match arg.kind { + ExprKind::Closure(closure) => { + let body = cx.tcx.hir_body(closure.body); + snippet_with_applicability(cx, body.value.span, "..", &mut applicability) + }, + ExprKind::Path(_) => snippet_with_applicability(cx, arg.span, "_", &mut applicability) + "()", + _ => return, }, - sym::unwrap_or_default => "Default::default()".into(), - _ => return, + Unwrap::OrDefault => "Default::default()".into(), }; let sugg = format!( @@ -83,3 +81,18 @@ pub(super) fn check<'tcx>( ); } } + +pub(super) enum Unwrap<'tcx> { + Or(&'tcx Expr<'tcx>), + OrElse(&'tcx Expr<'tcx>), + OrDefault, +} + +impl<'tcx> Unwrap<'tcx> { + fn arg(&self) -> Option<&'tcx Expr<'tcx>> { + match self { + Self::Or(a) | Self::OrElse(a) => Some(a), + Self::OrDefault => None, + } + } +} From 80b886e8958f5a2b4afa210ec169a28721b98cdf Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 8 Oct 2025 19:27:51 +0200 Subject: [PATCH 27/89] perf(get_unwrap): avoid calling `is_type_diagnostic_item` multiple times --- clippy_lints/src/methods/get_unwrap.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/methods/get_unwrap.rs b/clippy_lints/src/methods/get_unwrap.rs index 9daad1a8a949e..d273558a7006e 100644 --- a/clippy_lints/src/methods/get_unwrap.rs +++ b/clippy_lints/src/methods/get_unwrap.rs @@ -2,7 +2,6 @@ use super::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -22,16 +21,17 @@ pub(super) fn check<'tcx>( let expr_ty = cx.typeck_results().expr_ty(recv); let caller_type = if derefs_to_slice(cx, recv, expr_ty).is_some() { "slice" - } else if is_type_diagnostic_item(cx, expr_ty, sym::Vec) { - "Vec" - } else if is_type_diagnostic_item(cx, expr_ty, sym::VecDeque) { - "VecDeque" - } else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym::HashMap) { - "HashMap" - } else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym::BTreeMap) { - "BTreeMap" } else { - return; // caller is not a type that we want to lint + match expr_ty + .ty_adt_def() + .and_then(|def| cx.tcx.get_diagnostic_name(def.did())) + { + Some(sym::Vec) => "Vec", + Some(sym::VecDeque) => "VecDeque", + Some(sym::HashMap) if !is_mut => "HashMap", + Some(sym::BTreeMap) if !is_mut => "BTreeMap", + _ => return, // caller is not a type that we want to lint + } }; let mut span = expr.span; From 69bd890ff11768ba05f9f6d921c7914a56434db6 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 8 Oct 2025 17:39:37 +0200 Subject: [PATCH 28/89] Honor `allow`/`expect` attributes on ADT and `impl Clone` nodes --- .../src/derive/expl_impl_clone_on_copy.rs | 15 +++-- tests/ui/derive.rs | 15 ++++- tests/ui/derive.stderr | 55 ++----------------- 3 files changed, 28 insertions(+), 57 deletions(-) diff --git a/clippy_lints/src/derive/expl_impl_clone_on_copy.rs b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs index dfb723b86eb93..b2bc6402561f5 100644 --- a/clippy_lints/src/derive/expl_impl_clone_on_copy.rs +++ b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs @@ -1,4 +1,5 @@ -use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::fulfill_or_allowed; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_hir::{self as hir, HirId, Item}; use rustc_lint::LateContext; @@ -60,14 +61,16 @@ pub(super) fn check<'tcx>( return; } - span_lint_hir_and_then( + if fulfill_or_allowed(cx, EXPL_IMPL_CLONE_ON_COPY, [adt_hir_id]) { + return; + } + + span_lint_and_help( cx, EXPL_IMPL_CLONE_ON_COPY, - adt_hir_id, item.span, "you are implementing `Clone` explicitly on a `Copy` type", - |diag| { - diag.span_help(item.span, "consider deriving `Clone` or removing `Copy`"); - }, + None, + "consider deriving `Clone` or removing `Copy`", ); } diff --git a/tests/ui/derive.rs b/tests/ui/derive.rs index 305f73c92cd5c..036f6c444b649 100644 --- a/tests/ui/derive.rs +++ b/tests/ui/derive.rs @@ -132,7 +132,7 @@ fn issue14558() { fn main() {} mod issue15708 { - // Check that the lint posts on the type definition node + // Check that `allow`/`expect` attributes are recognized on the type definition node #[expect(clippy::expl_impl_clone_on_copy)] #[derive(Copy)] struct S; @@ -143,3 +143,16 @@ mod issue15708 { } } } + +mod issue15842 { + #[derive(Copy)] + struct S; + + // Check that `allow`/`expect` attributes are recognized on the `impl Clone` node + #[expect(clippy::expl_impl_clone_on_copy)] + impl Clone for S { + fn clone(&self) -> Self { + S + } + } +} diff --git a/tests/ui/derive.stderr b/tests/ui/derive.stderr index 48e55fc7469e4..2701680e788de 100644 --- a/tests/ui/derive.stderr +++ b/tests/ui/derive.stderr @@ -9,16 +9,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -help: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:15:1 - | -LL | / impl Clone for Qux { -LL | | -LL | | -LL | | fn clone(&self) -> Self { -... | -LL | | } - | |_^ + = help: consider deriving `Clone` or removing `Copy` = note: `-D clippy::expl-impl-clone-on-copy` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]` @@ -33,16 +24,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -help: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:41:1 - | -LL | / impl<'a> Clone for Lt<'a> { -LL | | -LL | | -LL | | fn clone(&self) -> Self { -... | -LL | | } - | |_^ + = help: consider deriving `Clone` or removing `Copy` error: you are implementing `Clone` explicitly on a `Copy` type --> tests/ui/derive.rs:54:1 @@ -55,16 +37,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -help: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:54:1 - | -LL | / impl Clone for BigArray { -LL | | -LL | | -LL | | fn clone(&self) -> Self { -... | -LL | | } - | |_^ + = help: consider deriving `Clone` or removing `Copy` error: you are implementing `Clone` explicitly on a `Copy` type --> tests/ui/derive.rs:67:1 @@ -77,16 +50,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -help: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:67:1 - | -LL | / impl Clone for FnPtr { -LL | | -LL | | -LL | | fn clone(&self) -> Self { -... | -LL | | } - | |_^ + = help: consider deriving `Clone` or removing `Copy` error: you are implementing `Clone` explicitly on a `Copy` type --> tests/ui/derive.rs:89:1 @@ -99,16 +63,7 @@ LL | | fn clone(&self) -> Self { LL | | } | |_^ | -help: consider deriving `Clone` or removing `Copy` - --> tests/ui/derive.rs:89:1 - | -LL | / impl Clone for Generic2 { -LL | | -LL | | -LL | | fn clone(&self) -> Self { -... | -LL | | } - | |_^ + = help: consider deriving `Clone` or removing `Copy` error: aborting due to 5 previous errors From c425389f184199372c8b3c6febcbc5202a818253 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 23 Sep 2025 20:29:52 +0200 Subject: [PATCH 29/89] Cleanup: do not handle methods from several places Some methods lints were handled in the `methods` module outside the `check_methods()` function. --- clippy_lints/src/methods/clone_on_copy.rs | 22 +++------- clippy_lints/src/methods/clone_on_ref_ptr.rs | 13 +----- clippy_lints/src/methods/expect_fun_call.rs | 17 +++---- .../src/methods/inefficient_to_string.rs | 16 ++----- clippy_lints/src/methods/into_iter_on_ref.rs | 9 +--- clippy_lints/src/methods/mod.rs | 44 ++++++++++--------- clippy_utils/src/sym.rs | 2 + 7 files changed, 42 insertions(+), 81 deletions(-) diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index 52ae5b7d01b3a..2a0ae14a4b088 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -4,26 +4,14 @@ use clippy_utils::ty::is_copy; use rustc_errors::Applicability; use rustc_hir::{BindingMode, ByRef, Expr, ExprKind, MatchSource, Node, PatKind, QPath}; use rustc_lint::LateContext; +use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::print::with_forced_trimmed_paths; -use rustc_middle::ty::{self}; -use rustc_span::symbol::{Symbol, sym}; use super::CLONE_ON_COPY; /// Checks for the `CLONE_ON_COPY` lint. -pub(super) fn check( - cx: &LateContext<'_>, - expr: &Expr<'_>, - method_name: Symbol, - receiver: &Expr<'_>, - args: &[Expr<'_>], -) { - let arg = if method_name == sym::clone && args.is_empty() { - receiver - } else { - return; - }; +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) { if cx .typeck_results() .type_dependent_def_id(expr.hir_id) @@ -33,10 +21,10 @@ pub(super) fn check( { return; } - let arg_adjustments = cx.typeck_results().expr_adjustments(arg); + let arg_adjustments = cx.typeck_results().expr_adjustments(receiver); let arg_ty = arg_adjustments .last() - .map_or_else(|| cx.typeck_results().expr_ty(arg), |a| a.target); + .map_or_else(|| cx.typeck_results().expr_ty(receiver), |a| a.target); let ty = cx.typeck_results().expr_ty(expr); if let ty::Ref(_, inner, _) = arg_ty.kind() @@ -75,7 +63,7 @@ pub(super) fn check( }; let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, arg.span, expr.span.ctxt(), "_", &mut app).0; + let snip = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "_", &mut app).0; let deref_count = arg_adjustments .iter() diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs index 65583c6a9811e..059893b05bb9c 100644 --- a/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -4,20 +4,11 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::symbol::sym; use super::CLONE_ON_REF_PTR; -pub(super) fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - method_name: Symbol, - receiver: &hir::Expr<'_>, - args: &[hir::Expr<'_>], -) { - if !(args.is_empty() && method_name == sym::clone) { - return; - } +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>) { let obj_ty = cx.typeck_results().expr_ty(receiver).peel_refs(); if let ty::Adt(adt, subst) = obj_ty.kind() diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index 818e26f8aa1df..74920a17310d5 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -7,8 +7,8 @@ use clippy_utils::{contains_return, is_inside_always_const_context, peel_blocks} use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::Span; use rustc_span::symbol::sym; -use rustc_span::{Span, Symbol}; use std::borrow::Cow; use std::ops::ControlFlow; @@ -20,16 +20,11 @@ pub(super) fn check<'tcx>( format_args_storage: &FormatArgsStorage, expr: &hir::Expr<'_>, method_span: Span, - name: Symbol, receiver: &'tcx hir::Expr<'tcx>, - args: &'tcx [hir::Expr<'tcx>], + arg: &'tcx hir::Expr<'tcx>, ) { - if name == sym::expect - && let [arg] = args - && let arg_root = get_arg_root(cx, arg) - && contains_call(cx, arg_root) - && !contains_return(arg_root) - { + let arg_root = get_arg_root(cx, arg); + if contains_call(cx, arg_root) && !contains_return(arg_root) { let receiver_type = cx.typeck_results().expr_ty_adjusted(receiver); let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym::Option) { "||" @@ -54,7 +49,7 @@ pub(super) fn check<'tcx>( cx, EXPECT_FUN_CALL, span_replace_word, - format!("function call inside of `{name}`"), + "function call inside of `expect`", "try", format!("unwrap_or_else({closure_args} panic!({sugg}))"), applicability, @@ -69,7 +64,7 @@ pub(super) fn check<'tcx>( cx, EXPECT_FUN_CALL, span_replace_word, - format!("function call inside of `{name}`"), + "function call inside of `expect`", "try", format!("unwrap_or_else({closure_args} panic!(\"{{}}\", {arg_root_snippet}))"), applicability, diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index ab21515f47f78..1b350b7abb619 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -6,22 +6,12 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_span::symbol::{Symbol, sym}; +use rustc_span::symbol::sym; use super::INEFFICIENT_TO_STRING; -/// Checks for the `INEFFICIENT_TO_STRING` lint -pub fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - method_name: Symbol, - receiver: &hir::Expr<'_>, - args: &[hir::Expr<'_>], - msrv: Msrv, -) { - if args.is_empty() - && method_name == sym::to_string - && let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) +pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, msrv: Msrv) { + if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && cx.tcx.is_diagnostic_item(sym::to_string_method, to_string_meth_did) && let Some(args) = cx.typeck_results().node_args_opt(expr.hir_id) && let arg_ty = cx.typeck_results().expr_ty_adjusted(receiver) diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs index bedeb63367d06..661e2824144c0 100644 --- a/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -10,16 +10,9 @@ use rustc_span::symbol::{Symbol, sym}; use super::INTO_ITER_ON_REF; -pub(super) fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - method_span: Span, - method_name: Symbol, - receiver: &hir::Expr<'_>, -) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Span, receiver: &hir::Expr<'_>) { let self_ty = cx.typeck_results().expr_ty_adjusted(receiver); if let ty::Ref(..) = self_ty.kind() - && method_name == sym::into_iter && is_trait_method(cx, expr, sym::IntoIterator) && let Some((kind, method_name)) = ty_has_iter_method(cx, self_ty) { diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a8f1ec7e91986..c2259c89cab09 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4842,8 +4842,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { return; } - self.check_methods(cx, expr); - match expr.kind { ExprKind::Call(func, args) => { from_iter_instead_of_collect::check(cx, expr, args, func); @@ -4854,24 +4852,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { swap_with_temporary::check(cx, expr, func, args); ip_constant::check(cx, expr, func, args); }, - ExprKind::MethodCall(method_call, receiver, args, _) => { - let method_span = method_call.ident.span; - or_fun_call::check(cx, expr, method_span, method_call.ident.name, receiver, args, self.msrv); - expect_fun_call::check( - cx, - &self.format_args, - expr, - method_span, - method_call.ident.name, - receiver, - args, - ); - clone_on_copy::check(cx, expr, method_call.ident.name, receiver, args); - clone_on_ref_ptr::check(cx, expr, method_call.ident.name, receiver, args); - inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args, self.msrv); - single_char_add_str::check(cx, expr, receiver, args); - into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver); - unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv); + ExprKind::MethodCall(..) => { + self.check_methods(cx, expr); }, ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { let mut info = BinaryExprInfo { @@ -5566,8 +5548,18 @@ impl Methods { } // Handle method calls whose receiver and arguments may come from expansion if let ExprKind::MethodCall(path, recv, args, _call_span) = expr.kind { + let method_span = path.ident.span; + + // Those methods do their own method name checking as they deal with multiple methods. + or_fun_call::check(cx, expr, method_span, path.ident.name, recv, args, self.msrv); + unnecessary_to_owned::check(cx, expr, path.ident.name, recv, args, self.msrv); + match (path.ident.name, args) { - (sym::expect, [_]) => { + (sym::clone, []) => { + clone_on_ref_ptr::check(cx, expr, recv); + clone_on_copy::check(cx, expr, recv); + }, + (sym::expect, [arg]) => { unwrap_expect_used::check( cx, expr, @@ -5577,6 +5569,7 @@ impl Methods { self.allow_expect_in_tests, unwrap_expect_used::Variant::Expect, ); + expect_fun_call::check(cx, &self.format_args, expr, method_span, recv, arg); }, (sym::expect_err, [_]) => { unwrap_expect_used::check( @@ -5589,6 +5582,15 @@ impl Methods { unwrap_expect_used::Variant::Expect, ); }, + (sym::insert_str | sym::push_str, _) => { + single_char_add_str::check(cx, expr, recv, args); + }, + (sym::into_iter, []) => { + into_iter_on_ref::check(cx, expr, method_span, recv); + }, + (sym::to_string, []) => { + inefficient_to_string::check(cx, expr, recv, self.msrv); + }, (sym::unwrap, []) => { unwrap_expect_used::check( cx, diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index fee45c6709623..2b22f344e8c02 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -176,6 +176,7 @@ generate! { hidden_glob_reexports, hygiene, insert, + insert_str, inspect, int_roundings, into, @@ -259,6 +260,7 @@ generate! { powi, product, push, + push_str, read, read_exact, read_line, From 6b697dba796e7fa3ed413052b55b79e1e154481a Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 23 Sep 2025 13:46:56 +0200 Subject: [PATCH 30/89] fix(clone_on_ref_ptr): only name the generic type if possible --- clippy_lints/src/methods/clone_on_ref_ptr.rs | 24 +++++++++++----- tests/ui/clone_on_ref_ptr.fixed | 30 ++++++++++++++++++++ tests/ui/clone_on_ref_ptr.rs | 30 ++++++++++++++++++++ tests/ui/clone_on_ref_ptr.stderr | 8 +++++- 4 files changed, 84 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs index 059893b05bb9c..238e1fe988b39 100644 --- a/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -3,7 +3,7 @@ use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_middle::ty; +use rustc_middle::ty::{self, IsSuggestable}; use rustc_span::symbol::sym; use super::CLONE_ON_REF_PTR; @@ -30,12 +30,22 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir:: // Sometimes unnecessary ::<_> after Rc/Arc/Weak let mut app = Applicability::Unspecified; let snippet = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "..", &mut app).0; - diag.span_suggestion( - expr.span, - "try", - format!("{caller_type}::<{}>::clone(&{snippet})", subst.type_at(0)), - app, - ); + let generic = subst.type_at(0); + if generic.is_suggestable(cx.tcx, true) { + diag.span_suggestion( + expr.span, + "try", + format!("{caller_type}::<{generic}>::clone(&{snippet})"), + app, + ); + } else { + diag.span_suggestion( + expr.span, + "try", + format!("{caller_type}::::clone(&{snippet})"), + Applicability::HasPlaceholders, + ); + } }, ); } diff --git a/tests/ui/clone_on_ref_ptr.fixed b/tests/ui/clone_on_ref_ptr.fixed index 8ef4b36566363..ede9d171517e7 100644 --- a/tests/ui/clone_on_ref_ptr.fixed +++ b/tests/ui/clone_on_ref_ptr.fixed @@ -49,3 +49,33 @@ mod issue2076 { //~^ clone_on_ref_ptr } } + +#[allow( + clippy::needless_borrow, + reason = "the suggestion creates `Weak::clone(&rec)`, but `rec` is already a reference" +)] +mod issue15009 { + use std::rc::{Rc, Weak}; + use std::sync::atomic::{AtomicU32, Ordering}; + + fn main() { + let counter = AtomicU32::new(0); + let counter_ref = &counter; + let factorial = Rc::new_cyclic(move |rec| { + let rec = std::rc::Weak::::clone(&rec) as Weak u32>; + //~^ clone_on_ref_ptr + move |x| { + // can capture env + counter_ref.fetch_add(1, Ordering::Relaxed); + match x { + 0 => 1, + x => x * rec.upgrade().unwrap()(x - 1), + } + } + }); + println!("{}", factorial(5)); // 120 + println!("{}", counter.load(Ordering::Relaxed)); // 6 + println!("{}", factorial(7)); // 5040 + println!("{}", counter.load(Ordering::Relaxed)); // 14 + } +} diff --git a/tests/ui/clone_on_ref_ptr.rs b/tests/ui/clone_on_ref_ptr.rs index fbd787099aee3..5999b4069d0f2 100644 --- a/tests/ui/clone_on_ref_ptr.rs +++ b/tests/ui/clone_on_ref_ptr.rs @@ -49,3 +49,33 @@ mod issue2076 { //~^ clone_on_ref_ptr } } + +#[allow( + clippy::needless_borrow, + reason = "the suggestion creates `Weak::clone(&rec)`, but `rec` is already a reference" +)] +mod issue15009 { + use std::rc::{Rc, Weak}; + use std::sync::atomic::{AtomicU32, Ordering}; + + fn main() { + let counter = AtomicU32::new(0); + let counter_ref = &counter; + let factorial = Rc::new_cyclic(move |rec| { + let rec = rec.clone() as Weak u32>; + //~^ clone_on_ref_ptr + move |x| { + // can capture env + counter_ref.fetch_add(1, Ordering::Relaxed); + match x { + 0 => 1, + x => x * rec.upgrade().unwrap()(x - 1), + } + } + }); + println!("{}", factorial(5)); // 120 + println!("{}", counter.load(Ordering::Relaxed)); // 6 + println!("{}", factorial(7)); // 5040 + println!("{}", counter.load(Ordering::Relaxed)); // 14 + } +} diff --git a/tests/ui/clone_on_ref_ptr.stderr b/tests/ui/clone_on_ref_ptr.stderr index b15f0e803a352..b8ddc3058c012 100644 --- a/tests/ui/clone_on_ref_ptr.stderr +++ b/tests/ui/clone_on_ref_ptr.stderr @@ -37,5 +37,11 @@ error: using `.clone()` on a ref-counted pointer LL | Some(try_opt!(Some(rc)).clone()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&try_opt!(Some(rc)))` -error: aborting due to 6 previous errors +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:65:23 + | +LL | let rec = rec.clone() as Weak u32>; + | ^^^^^^^^^^^ help: try: `std::rc::Weak::::clone(&rec)` + +error: aborting due to 7 previous errors From 6d0fafd8493f4250e7394b832391d6cc5c507fcf Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Oct 2025 11:15:05 +0200 Subject: [PATCH 31/89] clean-up --- .../src/matches/match_like_matches.rs | 43 ++++++++++--------- ...o.fixed => match_like_matches_macro.fixed} | 5 +-- ...s_macro.rs => match_like_matches_macro.rs} | 5 +-- ...stderr => match_like_matches_macro.stderr} | 28 ++++++------ 4 files changed, 41 insertions(+), 40 deletions(-) rename tests/ui/{match_expr_like_matches_macro.fixed => match_like_matches_macro.fixed} (97%) rename tests/ui/{match_expr_like_matches_macro.rs => match_like_matches_macro.rs} (97%) rename tests/ui/{match_expr_like_matches_macro.stderr => match_like_matches_macro.stderr} (84%) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 5816da5695eb6..8257c39c90f05 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -1,3 +1,5 @@ +//! Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!` + use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; @@ -11,7 +13,6 @@ use rustc_span::source_map::Spanned; use super::MATCH_LIKE_MATCHES_MACRO; -/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!` pub(crate) fn check_if_let<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -23,10 +24,11 @@ pub(crate) fn check_if_let<'tcx>( find_matches_sugg( cx, let_expr, - IntoIterator::into_iter([ + [ (&[][..], Some(let_pat), then_expr, None), (&[][..], None, else_expr, None), - ]), + ] + .into_iter(), expr, true, ); @@ -52,7 +54,7 @@ pub(super) fn check_match<'tcx>( fn find_matches_sugg<'a, 'b, I>( cx: &LateContext<'_>, ex: &Expr<'_>, - mut iter: I, + mut arms: I, expr: &Expr<'_>, is_if_let: bool, ) -> bool @@ -64,17 +66,17 @@ where + Iterator>, &'a Expr<'b>, Option<&'a Expr<'b>>)>, { if !span_contains_comment(cx.sess().source_map(), expr.span) - && iter.len() >= 2 + && arms.len() >= 2 && cx.typeck_results().expr_ty(expr).is_bool() - && let Some((_, last_pat_opt, last_expr, _)) = iter.next_back() - && let iter_without_last = iter.clone() - && let Some((first_attrs, _, first_expr, first_guard)) = iter.next() - && let Some(b0) = find_bool_lit(&first_expr.kind) - && let Some(b1) = find_bool_lit(&last_expr.kind) + && let Some((_, last_pat_opt, last_expr, _)) = arms.next_back() + && let arms_without_last = arms.clone() + && let Some((first_attrs, _, first_expr, first_guard)) = arms.next() + && let Some(b0) = find_bool_lit(first_expr) + && let Some(b1) = find_bool_lit(last_expr) && b0 != b1 - && (first_guard.is_none() || iter.len() == 0) + && (first_guard.is_none() || arms.len() == 0) && first_attrs.is_empty() - && iter.all(|arm| find_bool_lit(&arm.2.kind).is_some_and(|b| b == b0) && arm.3.is_none() && arm.0.is_empty()) + && arms.all(|(attrs, _, expr, guard)| attrs.is_empty() && guard.is_none() && find_bool_lit(expr) == Some(b0)) { if let Some(last_pat) = last_pat_opt && !is_wild(last_pat) @@ -82,10 +84,10 @@ where return false; } - for arm in iter_without_last.clone() { + for arm in arms_without_last.clone() { if let Some(pat) = arm.1 && !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) - && is_some(pat.kind) + && is_some_wild(pat.kind) { return false; } @@ -96,7 +98,7 @@ where let mut applicability = Applicability::MaybeIncorrect; let pat = { use itertools::Itertools as _; - iter_without_last + arms_without_last .filter_map(|arm| { let pat_span = arm.1?.span; Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability)) @@ -142,11 +144,11 @@ where } /// Extract a `bool` or `{ bool }` -fn find_bool_lit(ex: &ExprKind<'_>) -> Option { - match ex { +fn find_bool_lit(ex: &Expr<'_>) -> Option { + match ex.kind { ExprKind::Lit(Spanned { node: LitKind::Bool(b), .. - }) => Some(*b), + }) => Some(b), ExprKind::Block( rustc_hir::Block { stmts: [], @@ -168,8 +170,9 @@ fn find_bool_lit(ex: &ExprKind<'_>) -> Option { } } -fn is_some(path_kind: PatKind<'_>) -> bool { - match path_kind { +/// Checks whether a pattern is `Some(_)` +fn is_some_wild(pat_kind: PatKind<'_>) -> bool { + match pat_kind { PatKind::TupleStruct(QPath::Resolved(_, path), [first, ..], _) if is_wild(first) => { let name = path.segments[0].ident; name.name == rustc_span::sym::Some diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_like_matches_macro.fixed similarity index 97% rename from tests/ui/match_expr_like_matches_macro.fixed rename to tests/ui/match_like_matches_macro.fixed index 8530ab16bfd7d..a1c95e8a94f14 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_like_matches_macro.fixed @@ -1,7 +1,6 @@ #![warn(clippy::match_like_matches_macro)] #![allow( unreachable_patterns, - dead_code, clippy::equatable_if_let, clippy::needless_borrowed_reference, clippy::redundant_guards @@ -14,11 +13,11 @@ fn main() { let _y = matches!(x, Some(0)); //~^^^^ match_like_matches_macro - // Lint + // No lint: covered by `redundant_pattern_matching` let _w = x.is_some(); //~^^^^ redundant_pattern_matching - // Turn into is_none + // No lint: covered by `redundant_pattern_matching` let _z = x.is_none(); //~^^^^ redundant_pattern_matching diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_like_matches_macro.rs similarity index 97% rename from tests/ui/match_expr_like_matches_macro.rs rename to tests/ui/match_like_matches_macro.rs index 81017936889e4..eb419ba5bf8de 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_like_matches_macro.rs @@ -1,7 +1,6 @@ #![warn(clippy::match_like_matches_macro)] #![allow( unreachable_patterns, - dead_code, clippy::equatable_if_let, clippy::needless_borrowed_reference, clippy::redundant_guards @@ -17,14 +16,14 @@ fn main() { }; //~^^^^ match_like_matches_macro - // Lint + // No lint: covered by `redundant_pattern_matching` let _w = match x { Some(_) => true, _ => false, }; //~^^^^ redundant_pattern_matching - // Turn into is_none + // No lint: covered by `redundant_pattern_matching` let _z = match x { Some(_) => false, None => true, diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_like_matches_macro.stderr similarity index 84% rename from tests/ui/match_expr_like_matches_macro.stderr rename to tests/ui/match_like_matches_macro.stderr index 8fceb05bc6e87..ae277ce4dca68 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_like_matches_macro.stderr @@ -1,5 +1,5 @@ error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:14:14 + --> tests/ui/match_like_matches_macro.rs:13:14 | LL | let _y = match x { | ______________^ @@ -12,7 +12,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::match_like_matches_macro)]` error: redundant pattern matching, consider using `is_some()` - --> tests/ui/match_expr_like_matches_macro.rs:21:14 + --> tests/ui/match_like_matches_macro.rs:20:14 | LL | let _w = match x { | ______________^ @@ -25,7 +25,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]` error: redundant pattern matching, consider using `is_none()` - --> tests/ui/match_expr_like_matches_macro.rs:28:14 + --> tests/ui/match_like_matches_macro.rs:27:14 | LL | let _z = match x { | ______________^ @@ -35,7 +35,7 @@ LL | | }; | |_____^ help: try: `x.is_none()` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:35:15 + --> tests/ui/match_like_matches_macro.rs:34:15 | LL | let _zz = match x { | _______________^ @@ -45,13 +45,13 @@ LL | | }; | |_____^ help: try: `!matches!(x, Some(r) if r == 0)` error: if let .. else expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:42:16 + --> tests/ui/match_like_matches_macro.rs:41:16 | LL | let _zzz = if let Some(5) = x { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(x, Some(5))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:67:20 + --> tests/ui/match_like_matches_macro.rs:66:20 | LL | let _ans = match x { | ____________________^ @@ -62,7 +62,7 @@ LL | | }; | |_________^ help: try: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:78:20 + --> tests/ui/match_like_matches_macro.rs:77:20 | LL | let _ans = match x { | ____________________^ @@ -74,7 +74,7 @@ LL | | }; | |_________^ help: try: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:89:20 + --> tests/ui/match_like_matches_macro.rs:88:20 | LL | let _ans = match x { | ____________________^ @@ -85,7 +85,7 @@ LL | | }; | |_________^ help: try: `!matches!(x, E::B(_) | E::C)` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:150:18 + --> tests/ui/match_like_matches_macro.rs:149:18 | LL | let _z = match &z { | __________________^ @@ -95,7 +95,7 @@ LL | | }; | |_________^ help: try: `matches!(z, Some(3))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:160:18 + --> tests/ui/match_like_matches_macro.rs:159:18 | LL | let _z = match &z { | __________________^ @@ -105,7 +105,7 @@ LL | | }; | |_________^ help: try: `matches!(&z, Some(3))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:178:21 + --> tests/ui/match_like_matches_macro.rs:177:21 | LL | let _ = match &z { | _____________________^ @@ -115,7 +115,7 @@ LL | | }; | |_____________^ help: try: `matches!(&z, AnEnum::X)` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:193:20 + --> tests/ui/match_like_matches_macro.rs:192:20 | LL | let _res = match &val { | ____________________^ @@ -125,7 +125,7 @@ LL | | }; | |_________^ help: try: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:206:20 + --> tests/ui/match_like_matches_macro.rs:205:20 | LL | let _res = match &val { | ____________________^ @@ -135,7 +135,7 @@ LL | | }; | |_________^ help: try: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> tests/ui/match_expr_like_matches_macro.rs:265:14 + --> tests/ui/match_like_matches_macro.rs:264:14 | LL | let _y = match Some(5) { | ______________^ From 62e1225c87852d9650e6d34ffc6c263ba41fab8e Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Oct 2025 18:56:08 +0200 Subject: [PATCH 32/89] inline `find_matches_sugg` into `check_if_let` --- .../src/matches/match_like_matches.rs | 69 ++++++++++++++++--- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 8257c39c90f05..3df23a6357942 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -21,17 +21,64 @@ pub(crate) fn check_if_let<'tcx>( then_expr: &'tcx Expr<'_>, else_expr: &'tcx Expr<'_>, ) { - find_matches_sugg( - cx, - let_expr, - [ - (&[][..], Some(let_pat), then_expr, None), - (&[][..], None, else_expr, None), - ] - .into_iter(), - expr, - true, - ); + let mut arms = [(Some(let_pat), then_expr), (None, else_expr)].into_iter(); + let is_if_let = true; + if !span_contains_comment(cx.sess().source_map(), expr.span) + && cx.typeck_results().expr_ty(expr).is_bool() + && let Some((None, else_expr)) = arms.next_back() + && let arms_without_last = [(Some(let_pat), then_expr)] + && let Some((_, first_expr)) = arms.next() + && let Some(b0) = find_bool_lit(first_expr) + && let Some(b1) = find_bool_lit(else_expr) + && b0 != b1 + && arms.all(|(_, expr)| find_bool_lit(expr) == Some(b0)) + { + for arm in &arms_without_last { + if let Some(pat) = arm.0 + && !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) + && is_some_wild(pat.kind) + { + return; + } + } + + // The suggestion may be incorrect, because some arms can have `cfg` attributes + // evaluated into `false` and so such arms will be stripped before. + let mut applicability = Applicability::MaybeIncorrect; + let pat = { + use itertools::Itertools as _; + arms_without_last + .into_iter() + .filter_map(|arm| arm.0) + .map(|pat| snippet_with_applicability(cx, pat.span, "..", &mut applicability)) + .join(" | ") + }; + let pat_and_guard = pat; + + // strip potential borrows (#6503), but only if the type is a reference + let mut ex_new = let_expr; + if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = let_expr.kind + && let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() + { + ex_new = ex_inner; + } + span_lint_and_sugg( + cx, + MATCH_LIKE_MATCHES_MACRO, + expr.span, + format!( + "{} expression looks like `matches!` macro", + if is_if_let { "if let .. else" } else { "match" } + ), + "try", + format!( + "{}matches!({}, {pat_and_guard})", + if b0 { "" } else { "!" }, + snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), + ), + applicability, + ); + } } pub(super) fn check_match<'tcx>( From 27d5f5c7a61aca226eb33c8fa1e46b9ad4aa1f03 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Oct 2025 19:01:33 +0200 Subject: [PATCH 33/89] inline `is_if_let` into all callers --- .../src/matches/match_like_matches.rs | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 3df23a6357942..7e9a14c81fc3c 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -22,7 +22,6 @@ pub(crate) fn check_if_let<'tcx>( else_expr: &'tcx Expr<'_>, ) { let mut arms = [(Some(let_pat), then_expr), (None, else_expr)].into_iter(); - let is_if_let = true; if !span_contains_comment(cx.sess().source_map(), expr.span) && cx.typeck_results().expr_ty(expr).is_bool() && let Some((None, else_expr)) = arms.next_back() @@ -66,10 +65,7 @@ pub(crate) fn check_if_let<'tcx>( cx, MATCH_LIKE_MATCHES_MACRO, expr.span, - format!( - "{} expression looks like `matches!` macro", - if is_if_let { "if let .. else" } else { "match" } - ), + "if let .. else expression looks like `matches!` macro", "try", format!( "{}matches!({}, {pat_and_guard})", @@ -93,18 +89,11 @@ pub(super) fn check_match<'tcx>( arms.iter() .map(|arm| (cx.tcx.hir_attrs(arm.hir_id), Some(arm.pat), arm.body, arm.guard)), e, - false, ) } /// Lint a `match` or `if let` for replacement by `matches!` -fn find_matches_sugg<'a, 'b, I>( - cx: &LateContext<'_>, - ex: &Expr<'_>, - mut arms: I, - expr: &Expr<'_>, - is_if_let: bool, -) -> bool +fn find_matches_sugg<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, mut arms: I, expr: &Expr<'_>) -> bool where 'b: 'a, I: Clone @@ -172,10 +161,7 @@ where cx, MATCH_LIKE_MATCHES_MACRO, expr.span, - format!( - "{} expression looks like `matches!` macro", - if is_if_let { "if let .. else" } else { "match" } - ), + "match expression looks like `matches!` macro", "try", format!( "{}matches!({}, {pat_and_guard})", From 8519d49d6f5eabed9ddd0a241436cc1d80e46022 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Oct 2025 19:04:02 +0200 Subject: [PATCH 34/89] inline `arms_without_last` into all callers it's now just the first arm --- .../src/matches/match_like_matches.rs | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 7e9a14c81fc3c..7b5412d297365 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -25,34 +25,20 @@ pub(crate) fn check_if_let<'tcx>( if !span_contains_comment(cx.sess().source_map(), expr.span) && cx.typeck_results().expr_ty(expr).is_bool() && let Some((None, else_expr)) = arms.next_back() - && let arms_without_last = [(Some(let_pat), then_expr)] && let Some((_, first_expr)) = arms.next() && let Some(b0) = find_bool_lit(first_expr) && let Some(b1) = find_bool_lit(else_expr) && b0 != b1 && arms.all(|(_, expr)| find_bool_lit(expr) == Some(b0)) { - for arm in &arms_without_last { - if let Some(pat) = arm.0 - && !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) - && is_some_wild(pat.kind) - { - return; - } + if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, let_pat.hir_id) && is_some_wild(let_pat.kind) { + return; } // The suggestion may be incorrect, because some arms can have `cfg` attributes // evaluated into `false` and so such arms will be stripped before. let mut applicability = Applicability::MaybeIncorrect; - let pat = { - use itertools::Itertools as _; - arms_without_last - .into_iter() - .filter_map(|arm| arm.0) - .map(|pat| snippet_with_applicability(cx, pat.span, "..", &mut applicability)) - .join(" | ") - }; - let pat_and_guard = pat; + let pat = snippet_with_applicability(cx, let_pat.span, "..", &mut applicability); // strip potential borrows (#6503), but only if the type is a reference let mut ex_new = let_expr; @@ -68,7 +54,7 @@ pub(crate) fn check_if_let<'tcx>( "if let .. else expression looks like `matches!` macro", "try", format!( - "{}matches!({}, {pat_and_guard})", + "{}matches!({}, {pat})", if b0 { "" } else { "!" }, snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), ), From d1d5f0c7ae6944ff9d47aa97b0a3fb0a1469d2b0 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Oct 2025 19:11:02 +0200 Subject: [PATCH 35/89] inline `arms` into all callers --- clippy_lints/src/matches/match_like_matches.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 7b5412d297365..3bee0b7894f52 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -21,15 +21,11 @@ pub(crate) fn check_if_let<'tcx>( then_expr: &'tcx Expr<'_>, else_expr: &'tcx Expr<'_>, ) { - let mut arms = [(Some(let_pat), then_expr), (None, else_expr)].into_iter(); if !span_contains_comment(cx.sess().source_map(), expr.span) && cx.typeck_results().expr_ty(expr).is_bool() - && let Some((None, else_expr)) = arms.next_back() - && let Some((_, first_expr)) = arms.next() - && let Some(b0) = find_bool_lit(first_expr) + && let Some(b0) = find_bool_lit(then_expr) && let Some(b1) = find_bool_lit(else_expr) && b0 != b1 - && arms.all(|(_, expr)| find_bool_lit(expr) == Some(b0)) { if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, let_pat.hir_id) && is_some_wild(let_pat.kind) { return; From 83e2b3df5481ce50c898327f37ba57450fa009c1 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Oct 2025 19:14:10 +0200 Subject: [PATCH 36/89] inline `find_matches_sugg` into `check_match` --- .../src/matches/match_like_matches.rs | 33 +++++-------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 3bee0b7894f52..61b964a19f328 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -6,7 +6,7 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_lint_allowed, is_wild, span_contains_comment}; use rustc_ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{Arm, Attribute, BorrowKind, Expr, ExprKind, Pat, PatKind, QPath}; +use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Pat, PatKind, QPath}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; use rustc_span::source_map::Spanned; @@ -65,27 +65,12 @@ pub(super) fn check_match<'tcx>( scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'tcx>], ) -> bool { - find_matches_sugg( - cx, - scrutinee, - arms.iter() - .map(|arm| (cx.tcx.hir_attrs(arm.hir_id), Some(arm.pat), arm.body, arm.guard)), - e, - ) -} - -/// Lint a `match` or `if let` for replacement by `matches!` -fn find_matches_sugg<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, mut arms: I, expr: &Expr<'_>) -> bool -where - 'b: 'a, - I: Clone - + DoubleEndedIterator - + ExactSizeIterator - + Iterator>, &'a Expr<'b>, Option<&'a Expr<'b>>)>, -{ - if !span_contains_comment(cx.sess().source_map(), expr.span) + let mut arms = arms + .iter() + .map(|arm| (cx.tcx.hir_attrs(arm.hir_id), Some(arm.pat), arm.body, arm.guard)); + if !span_contains_comment(cx.sess().source_map(), e.span) && arms.len() >= 2 - && cx.typeck_results().expr_ty(expr).is_bool() + && cx.typeck_results().expr_ty(e).is_bool() && let Some((_, last_pat_opt, last_expr, _)) = arms.next_back() && let arms_without_last = arms.clone() && let Some((first_attrs, _, first_expr, first_guard)) = arms.next() @@ -133,8 +118,8 @@ where }; // strip potential borrows (#6503), but only if the type is a reference - let mut ex_new = ex; - if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind + let mut ex_new = scrutinee; + if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = scrutinee.kind && let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() { ex_new = ex_inner; @@ -142,7 +127,7 @@ where span_lint_and_sugg( cx, MATCH_LIKE_MATCHES_MACRO, - expr.span, + e.span, "match expression looks like `matches!` macro", "try", format!( From bf5170a54967664489161b90dfc75ba352a1cdc6 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Oct 2025 19:16:58 +0200 Subject: [PATCH 37/89] match arm pats aren't `Option`al anymore --- .../src/matches/match_like_matches.rs | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 61b964a19f328..9a2610364fefb 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -67,11 +67,11 @@ pub(super) fn check_match<'tcx>( ) -> bool { let mut arms = arms .iter() - .map(|arm| (cx.tcx.hir_attrs(arm.hir_id), Some(arm.pat), arm.body, arm.guard)); + .map(|arm| (cx.tcx.hir_attrs(arm.hir_id), arm.pat, arm.body, arm.guard)); if !span_contains_comment(cx.sess().source_map(), e.span) && arms.len() >= 2 && cx.typeck_results().expr_ty(e).is_bool() - && let Some((_, last_pat_opt, last_expr, _)) = arms.next_back() + && let Some((_, last_pat, last_expr, _)) = arms.next_back() && let arms_without_last = arms.clone() && let Some((first_attrs, _, first_expr, first_guard)) = arms.next() && let Some(b0) = find_bool_lit(first_expr) @@ -81,17 +81,13 @@ pub(super) fn check_match<'tcx>( && first_attrs.is_empty() && arms.all(|(attrs, _, expr, guard)| attrs.is_empty() && guard.is_none() && find_bool_lit(expr) == Some(b0)) { - if let Some(last_pat) = last_pat_opt - && !is_wild(last_pat) - { + if !is_wild(last_pat) { return false; } for arm in arms_without_last.clone() { - if let Some(pat) = arm.1 - && !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) - && is_some_wild(pat.kind) - { + let pat = arm.1; + if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) && is_some_wild(pat.kind) { return false; } } @@ -102,9 +98,9 @@ pub(super) fn check_match<'tcx>( let pat = { use itertools::Itertools as _; arms_without_last - .filter_map(|arm| { - let pat_span = arm.1?.span; - Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability)) + .map(|arm| { + let pat_span = arm.1.span; + snippet_with_applicability(cx, pat_span, "..", &mut applicability) }) .join(" | ") }; From 0efe3cfca56972c2117b1df7f109114264f4ff4d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Oct 2025 19:27:02 +0200 Subject: [PATCH 38/89] split `arms` into first, last, and middle pats as slice --- .../src/matches/match_like_matches.rs | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 9a2610364fefb..a91970f822906 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -65,28 +65,28 @@ pub(super) fn check_match<'tcx>( scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'tcx>], ) -> bool { - let mut arms = arms - .iter() - .map(|arm| (cx.tcx.hir_attrs(arm.hir_id), arm.pat, arm.body, arm.guard)); - if !span_contains_comment(cx.sess().source_map(), e.span) - && arms.len() >= 2 + if let Some((last_arm, arms_without_last)) = arms.split_last() + && let Some((first_arm, middle_arms)) = arms_without_last.split_first() + && !span_contains_comment(cx.sess().source_map(), e.span) && cx.typeck_results().expr_ty(e).is_bool() - && let Some((_, last_pat, last_expr, _)) = arms.next_back() - && let arms_without_last = arms.clone() - && let Some((first_attrs, _, first_expr, first_guard)) = arms.next() + && let (last_pat, last_expr) = (last_arm.pat, last_arm.body) + && let (first_attrs, first_expr, first_guard) = + (cx.tcx.hir_attrs(first_arm.hir_id), first_arm.body, first_arm.guard) && let Some(b0) = find_bool_lit(first_expr) && let Some(b1) = find_bool_lit(last_expr) && b0 != b1 - && (first_guard.is_none() || arms.len() == 0) + && (first_guard.is_none() || middle_arms.is_empty()) && first_attrs.is_empty() - && arms.all(|(attrs, _, expr, guard)| attrs.is_empty() && guard.is_none() && find_bool_lit(expr) == Some(b0)) + && middle_arms.iter().all(|arm| { + cx.tcx.hir_attrs(arm.hir_id).is_empty() && arm.guard.is_none() && find_bool_lit(arm.body) == Some(b0) + }) { if !is_wild(last_pat) { return false; } - for arm in arms_without_last.clone() { - let pat = arm.1; + for arm in arms_without_last { + let pat = arm.pat; if !is_lint_allowed(cx, REDUNDANT_PATTERN_MATCHING, pat.hir_id) && is_some_wild(pat.kind) { return false; } @@ -98,10 +98,8 @@ pub(super) fn check_match<'tcx>( let pat = { use itertools::Itertools as _; arms_without_last - .map(|arm| { - let pat_span = arm.1.span; - snippet_with_applicability(cx, pat_span, "..", &mut applicability) - }) + .iter() + .map(|arm| snippet_with_applicability(cx, arm.pat.span, "..", &mut applicability)) .join(" | ") }; let pat_and_guard = if let Some(g) = first_guard { From dd7f6058bf221dbd18eb8c47b283b3aaaf0bf4a4 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Oct 2025 19:28:49 +0200 Subject: [PATCH 39/89] inline `{first,last}_{attrs,expr,pat,guard}` into all callers --- clippy_lints/src/matches/match_like_matches.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index a91970f822906..89da55d4fe742 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -69,19 +69,16 @@ pub(super) fn check_match<'tcx>( && let Some((first_arm, middle_arms)) = arms_without_last.split_first() && !span_contains_comment(cx.sess().source_map(), e.span) && cx.typeck_results().expr_ty(e).is_bool() - && let (last_pat, last_expr) = (last_arm.pat, last_arm.body) - && let (first_attrs, first_expr, first_guard) = - (cx.tcx.hir_attrs(first_arm.hir_id), first_arm.body, first_arm.guard) - && let Some(b0) = find_bool_lit(first_expr) - && let Some(b1) = find_bool_lit(last_expr) + && let Some(b0) = find_bool_lit(first_arm.body) + && let Some(b1) = find_bool_lit(last_arm.body) && b0 != b1 - && (first_guard.is_none() || middle_arms.is_empty()) - && first_attrs.is_empty() + && (first_arm.guard.is_none() || middle_arms.is_empty()) + && cx.tcx.hir_attrs(first_arm.hir_id).is_empty() && middle_arms.iter().all(|arm| { cx.tcx.hir_attrs(arm.hir_id).is_empty() && arm.guard.is_none() && find_bool_lit(arm.body) == Some(b0) }) { - if !is_wild(last_pat) { + if !is_wild(last_arm.pat) { return false; } @@ -102,7 +99,7 @@ pub(super) fn check_match<'tcx>( .map(|arm| snippet_with_applicability(cx, arm.pat.span, "..", &mut applicability)) .join(" | ") }; - let pat_and_guard = if let Some(g) = first_guard { + let pat_and_guard = if let Some(g) = first_arm.guard { format!( "{pat} if {}", snippet_with_applicability(cx, g.span, "..", &mut applicability) From 05359084991fc484539db87cf9e70665d1ccb5dd Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Oct 2025 21:33:24 +0200 Subject: [PATCH 40/89] final refactor and docs --- .../src/matches/match_like_matches.rs | 64 +++++++++++++++++-- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 89da55d4fe742..b5f631e8fea33 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -72,11 +72,65 @@ pub(super) fn check_match<'tcx>( && let Some(b0) = find_bool_lit(first_arm.body) && let Some(b1) = find_bool_lit(last_arm.body) && b0 != b1 - && (first_arm.guard.is_none() || middle_arms.is_empty()) - && cx.tcx.hir_attrs(first_arm.hir_id).is_empty() - && middle_arms.iter().all(|arm| { - cx.tcx.hir_attrs(arm.hir_id).is_empty() && arm.guard.is_none() && find_bool_lit(arm.body) == Some(b0) - }) + // We handle two cases: + && ( + // - There are no middle arms, i.e., 2 arms in total + // + // In that case, the first arm may or may not have a guard, because this: + // ```rs + // match e { + // Either::Left $(if $guard)|+ => true, // or `false`, but then we'll need `!matches!(..)` + // _ => false, + // } + // ``` + // can always become this: + // ```rs + // matches!(e, Either::Left $(if $guard)|+) + // ``` + middle_arms.is_empty() + + // - (added in #6216) There are middle arms + // + // In that case, neither they nor the first arm may have guards + // -- otherwise, they couldn't be combined into an or-pattern in `matches!` + // + // This: + // ```rs + // match e { + // Either3::First => true, + // Either3::Second => true, + // _ /* matches `Either3::Third` */ => false, + // } + // ``` + // can become this: + // ```rs + // matches!(e, Either3::First | Either3::Second) + // ``` + // + // But this: + // ```rs + // match e { + // Either3::First if X => true, + // Either3::Second => true, + // _ => false, + // } + // ``` + // cannot be transformed. + // + // We set an additional constraint of all of them needing to return the same bool, + // so we don't lint things like: + // ```rs + // match e { + // Either3::First => true, + // Either3::Second => false, + // _ => false, + // } + // ``` + // This is not *strictly* necessary, but it simplifies the logic a bit + || arms_without_last.iter().all(|arm| { + cx.tcx.hir_attrs(arm.hir_id).is_empty() && arm.guard.is_none() && find_bool_lit(arm.body) == Some(b0) + }) + ) { if !is_wild(last_arm.pat) { return false; From 343e7806ea87bdeb1025af39d30bb0e94a34492a Mon Sep 17 00:00:00 2001 From: blyxyas Date: Thu, 9 Oct 2025 23:54:11 +0200 Subject: [PATCH 41/89] Mark blyxyas on vacation --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/triagebot.toml b/triagebot.toml index b2fb50918f583..57ff3dc352c8e 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -60,6 +60,7 @@ contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIB users_on_vacation = [ "matthiaskrgr", "Manishearth", + "blyxyas", ] [assign.owners] From b71fe9254d75707234349a2f0f4f50fad51389cc Mon Sep 17 00:00:00 2001 From: Nick Drozd Date: Mon, 25 Aug 2025 17:22:20 -0400 Subject: [PATCH 42/89] Check structs and enums for use_self --- CHANGELOG.md | 1 + book/src/lint_configuration.md | 10 ++ clippy_config/src/conf.rs | 3 + clippy_lints/src/matches/single_match.rs | 2 +- clippy_lints/src/use_self.rs | 19 ++- clippy_utils/src/consts.rs | 8 +- clippy_utils/src/sugg.rs | 2 +- .../toml_unknown_key/conf_unknown_key.stderr | 3 + tests/ui-toml/use_self/default/clippy.toml | 0 tests/ui-toml/use_self/disabled/clippy.toml | 1 + tests/ui-toml/use_self/use_self.default.fixed | 17 +++ .../ui-toml/use_self/use_self.default.stderr | 17 +++ .../ui-toml/use_self/use_self.disabled.fixed | 17 +++ .../ui-toml/use_self/use_self.disabled.stderr | 11 ++ tests/ui-toml/use_self/use_self.rs | 17 +++ tests/ui/use_self_structs.fixed | 134 ++++++++++++++++++ tests/ui/use_self_structs.rs | 134 ++++++++++++++++++ tests/ui/use_self_structs.stderr | 77 ++++++++++ 18 files changed, 463 insertions(+), 10 deletions(-) create mode 100644 tests/ui-toml/use_self/default/clippy.toml create mode 100644 tests/ui-toml/use_self/disabled/clippy.toml create mode 100644 tests/ui-toml/use_self/use_self.default.fixed create mode 100644 tests/ui-toml/use_self/use_self.default.stderr create mode 100644 tests/ui-toml/use_self/use_self.disabled.fixed create mode 100644 tests/ui-toml/use_self/use_self.disabled.stderr create mode 100644 tests/ui-toml/use_self/use_self.rs create mode 100644 tests/ui/use_self_structs.fixed create mode 100644 tests/ui/use_self_structs.rs create mode 100644 tests/ui/use_self_structs.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 1aaf617f5b151..b2e9f6d1dd3ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7052,6 +7052,7 @@ Released 2018-09-13 [`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv [`pass-by-value-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pass-by-value-size-limit [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior +[`recursive-self-in-type-definitions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#recursive-self-in-type-definitions [`semicolon-inside-block-ignore-singleline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-inside-block-ignore-singleline [`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 diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index b2ba19631f133..f0b2feca6a014 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -927,6 +927,16 @@ exported visibility, or whether they are marked as "pub". * [`pub_underscore_fields`](https://rust-lang.github.io/rust-clippy/master/index.html#pub_underscore_fields) +## `recursive-self-in-type-definitions` +Whether the type itself in a struct or enum should be replaced with `Self` when encountering recursive types. + +**Default Value:** `true` + +--- +**Affected lints:** +* [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self) + + ## `semicolon-inside-block-ignore-singleline` Whether to lint only if it's multiline. diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 843aa6a40f098..9902065422fbd 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -809,6 +809,9 @@ define_Conf! { /// exported visibility, or whether they are marked as "pub". #[lints(pub_underscore_fields)] pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported, + /// Whether the type itself in a struct or enum should be replaced with `Self` when encountering recursive types. + #[lints(use_self)] + recursive_self_in_type_definitions: bool = true, /// Whether to lint only if it's multiline. #[lints(semicolon_inside_block)] semicolon_inside_block_ignore_singleline: bool = false, diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 02f87512966ba..44c4d7a31ff31 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -224,7 +224,7 @@ enum PatState<'a> { /// A std enum we know won't be extended. Tracks the states of each variant separately. /// /// This is not used for `Option` since it uses the current pattern to track its state. - StdEnum(&'a mut [PatState<'a>]), + StdEnum(&'a mut [Self]), /// Either the initial state for a pattern or a non-std enum. There is currently no need to /// distinguish these cases. /// diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 9d5be922f43f8..f46dedf1261e9 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -58,6 +58,7 @@ declare_clippy_lint! { pub struct UseSelf { msrv: Msrv, stack: Vec, + recursive_self_in_type_definitions: bool, } impl UseSelf { @@ -65,6 +66,7 @@ impl UseSelf { Self { msrv: conf.msrv, stack: Vec::new(), + recursive_self_in_type_definitions: conf.recursive_self_in_type_definitions, } } } @@ -84,10 +86,10 @@ const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) { - // We push the self types of `impl`s on a stack here. Only the top type on the stack is - // relevant for linting, since this is the self type of the `impl` we're currently in. To - // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that - // we're in an `impl` or nested item, that we don't want to lint + // We push the self types of items on a stack here. Only the top type on the stack is + // relevant for linting, since this is the self type of the item we're currently in. To + // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal that + // we're in an item or nested item that we don't want to lint 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 @@ -112,6 +114,15 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { impl_id: item.owner_id.def_id, types_to_skip, } + } else if let ItemKind::Struct(..) | ItemKind::Enum(..) = item.kind + && self.recursive_self_in_type_definitions + && !item.span.from_expansion() + && !is_from_proc_macro(cx, item) + { + StackItem::Check { + impl_id: item.owner_id.def_id, + types_to_skip: FxHashSet::default(), + } } else { StackItem::NoCheck }; diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index e03ba2f73b437..6e2cabd5e61bf 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -50,15 +50,15 @@ pub enum Constant { /// `true` or `false`. Bool(bool), /// An array of constants. - Vec(Vec), + Vec(Vec), /// Also an array, but with only one constant, repeated N times. - Repeat(Box, u64), + Repeat(Box, u64), /// A tuple of constants. - Tuple(Vec), + Tuple(Vec), /// A raw pointer. RawPtr(u128), /// A reference - Ref(Box), + Ref(Box), /// A literal with syntax error. Err, } diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index a63333c9b48f9..581c2b02839dc 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -33,7 +33,7 @@ pub enum Sugg<'a> { /// or `-`, but only if the type with and without the operator is kept identical. /// It means that doubling the operator can be used to remove it instead, in /// order to provide better suggestions. - UnOp(UnOp, Box>), + UnOp(UnOp, Box), } /// Literal constant `0`, for convenience. diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 20aeb4bb8498e..b96a5486c72bd 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -66,6 +66,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect msrv pass-by-value-size-limit pub-underscore-fields-behavior + recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline semicolon-outside-block-ignore-multiline single-char-binding-names-threshold @@ -161,6 +162,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect msrv pass-by-value-size-limit pub-underscore-fields-behavior + recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline semicolon-outside-block-ignore-multiline single-char-binding-names-threshold @@ -256,6 +258,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni msrv pass-by-value-size-limit pub-underscore-fields-behavior + recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline semicolon-outside-block-ignore-multiline single-char-binding-names-threshold diff --git a/tests/ui-toml/use_self/default/clippy.toml b/tests/ui-toml/use_self/default/clippy.toml new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/tests/ui-toml/use_self/disabled/clippy.toml b/tests/ui-toml/use_self/disabled/clippy.toml new file mode 100644 index 0000000000000..866cc5c624b5f --- /dev/null +++ b/tests/ui-toml/use_self/disabled/clippy.toml @@ -0,0 +1 @@ +recursive-self-in-type-definitions = false diff --git a/tests/ui-toml/use_self/use_self.default.fixed b/tests/ui-toml/use_self/use_self.default.fixed new file mode 100644 index 0000000000000..288e304c60a8e --- /dev/null +++ b/tests/ui-toml/use_self/use_self.default.fixed @@ -0,0 +1,17 @@ +//@revisions: default disabled +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/default + +#![warn(clippy::use_self)] + +fn main() {} + +struct Basic { + flag: Option>, + //~[default]^ use_self +} + +impl Basic { + fn x(_: Self) {} + //~[default,disabled]^ use_self +} diff --git a/tests/ui-toml/use_self/use_self.default.stderr b/tests/ui-toml/use_self/use_self.default.stderr new file mode 100644 index 0000000000000..34cfdfd938aa1 --- /dev/null +++ b/tests/ui-toml/use_self/use_self.default.stderr @@ -0,0 +1,17 @@ +error: unnecessary structure name repetition + --> tests/ui-toml/use_self/use_self.rs:10:22 + | +LL | flag: Option>, + | ^^^^^ help: use the applicable keyword: `Self` + | + = note: `-D clippy::use-self` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::use_self)]` + +error: unnecessary structure name repetition + --> tests/ui-toml/use_self/use_self.rs:15:13 + | +LL | fn x(_: Basic) {} + | ^^^^^ help: use the applicable keyword: `Self` + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/use_self/use_self.disabled.fixed b/tests/ui-toml/use_self/use_self.disabled.fixed new file mode 100644 index 0000000000000..227606e69005e --- /dev/null +++ b/tests/ui-toml/use_self/use_self.disabled.fixed @@ -0,0 +1,17 @@ +//@revisions: default disabled +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/default + +#![warn(clippy::use_self)] + +fn main() {} + +struct Basic { + flag: Option>, + //~[default]^ use_self +} + +impl Basic { + fn x(_: Self) {} + //~[default,disabled]^ use_self +} diff --git a/tests/ui-toml/use_self/use_self.disabled.stderr b/tests/ui-toml/use_self/use_self.disabled.stderr new file mode 100644 index 0000000000000..1801744f0d417 --- /dev/null +++ b/tests/ui-toml/use_self/use_self.disabled.stderr @@ -0,0 +1,11 @@ +error: unnecessary structure name repetition + --> tests/ui-toml/use_self/use_self.rs:15:13 + | +LL | fn x(_: Basic) {} + | ^^^^^ help: use the applicable keyword: `Self` + | + = note: `-D clippy::use-self` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::use_self)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/use_self/use_self.rs b/tests/ui-toml/use_self/use_self.rs new file mode 100644 index 0000000000000..d20006d4df1ae --- /dev/null +++ b/tests/ui-toml/use_self/use_self.rs @@ -0,0 +1,17 @@ +//@revisions: default disabled +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/use_self/default + +#![warn(clippy::use_self)] + +fn main() {} + +struct Basic { + flag: Option>, + //~[default]^ use_self +} + +impl Basic { + fn x(_: Basic) {} + //~[default,disabled]^ use_self +} diff --git a/tests/ui/use_self_structs.fixed b/tests/ui/use_self_structs.fixed new file mode 100644 index 0000000000000..bd7bc3e0c1f69 --- /dev/null +++ b/tests/ui/use_self_structs.fixed @@ -0,0 +1,134 @@ +#![warn(clippy::use_self)] +#![allow(clippy::type_complexity)] + +fn main() {} + +struct Basic { + flag: Option>, + //~^ use_self +} + +struct BasicSelf { + okay: Option>, +} + +struct Generic<'q, T: From> { + t: &'q T, + flag: Option>, + //~^ use_self +} + +struct GenericSelf<'q, T: From> { + t: &'q T, + okay: Option>, +} + +struct MixedLifetimes<'q, T: From + 'static> { + t: &'q T, + okay: Option>>, +} + +struct ConcreteType<'q, T: From> { + t: &'q T, + okay: Option>>, +} + +struct ConcreteAndGeneric<'q, T: From> { + t: &'q T, + flag: Option>, + //~^ use_self + okay: Option>>, +} + +struct ConcreteAndGenericSelf<'q, T: From> { + t: &'q T, + okay_1: Option>, + okay_2: Option>>, +} + +macro_rules! recursive_struct { + ($name:ident) => { + struct $name { + okay: Option>, + } + }; +} + +recursive_struct!(X); +recursive_struct!(Y); +recursive_struct!(Z); + +struct Tree { + left: Option>, + //~^ use_self + right: Option>, + //~^ use_self +} + +struct TreeSelf { + left: Option>, + right: Option>, +} + +struct TreeMixed { + left: Option>, + right: Option>, + //~^ use_self +} + +struct Nested { + flag: Option>>>, + //~^ use_self +} + +struct NestedSelf { + okay: Option>>>, +} + +struct Tuple(Option>); +//~^ use_self + +struct TupleSelf(Option>); + +use std::cell::RefCell; +use std::rc::{Rc, Weak}; + +struct Containers { + flag: Vec>>>>>>, + //~^ use_self +} + +struct ContainersSelf { + okay: Vec>>>>>>, +} + +type Wrappers = Vec>>>>>>; + +struct Alias { + flag: Wrappers, + //~^ use_self +} + +struct AliasSelf { + okay: Wrappers, +} + +struct Array { + flag: [Option>; N], + //~^ use_self +} + +struct ArraySelf { + okay: [Option>; N], +} + +enum Enum { + Nil, + Cons(Box), + //~^ use_self +} + +enum EnumSelf { + Nil, + Cons(Box), +} diff --git a/tests/ui/use_self_structs.rs b/tests/ui/use_self_structs.rs new file mode 100644 index 0000000000000..624f158164ab0 --- /dev/null +++ b/tests/ui/use_self_structs.rs @@ -0,0 +1,134 @@ +#![warn(clippy::use_self)] +#![allow(clippy::type_complexity)] + +fn main() {} + +struct Basic { + flag: Option>, + //~^ use_self +} + +struct BasicSelf { + okay: Option>, +} + +struct Generic<'q, T: From> { + t: &'q T, + flag: Option>>, + //~^ use_self +} + +struct GenericSelf<'q, T: From> { + t: &'q T, + okay: Option>, +} + +struct MixedLifetimes<'q, T: From + 'static> { + t: &'q T, + okay: Option>>, +} + +struct ConcreteType<'q, T: From> { + t: &'q T, + okay: Option>>, +} + +struct ConcreteAndGeneric<'q, T: From> { + t: &'q T, + flag: Option>>, + //~^ use_self + okay: Option>>, +} + +struct ConcreteAndGenericSelf<'q, T: From> { + t: &'q T, + okay_1: Option>, + okay_2: Option>>, +} + +macro_rules! recursive_struct { + ($name:ident) => { + struct $name { + okay: Option>, + } + }; +} + +recursive_struct!(X); +recursive_struct!(Y); +recursive_struct!(Z); + +struct Tree { + left: Option>, + //~^ use_self + right: Option>, + //~^ use_self +} + +struct TreeSelf { + left: Option>, + right: Option>, +} + +struct TreeMixed { + left: Option>, + right: Option>, + //~^ use_self +} + +struct Nested { + flag: Option>>>, + //~^ use_self +} + +struct NestedSelf { + okay: Option>>>, +} + +struct Tuple(Option>); +//~^ use_self + +struct TupleSelf(Option>); + +use std::cell::RefCell; +use std::rc::{Rc, Weak}; + +struct Containers { + flag: Vec>>>>>>, + //~^ use_self +} + +struct ContainersSelf { + okay: Vec>>>>>>, +} + +type Wrappers = Vec>>>>>>; + +struct Alias { + flag: Wrappers, + //~^ use_self +} + +struct AliasSelf { + okay: Wrappers, +} + +struct Array { + flag: [Option>>; N], + //~^ use_self +} + +struct ArraySelf { + okay: [Option>; N], +} + +enum Enum { + Nil, + Cons(Box), + //~^ use_self +} + +enum EnumSelf { + Nil, + Cons(Box), +} diff --git a/tests/ui/use_self_structs.stderr b/tests/ui/use_self_structs.stderr new file mode 100644 index 0000000000000..766d1d4fd2c05 --- /dev/null +++ b/tests/ui/use_self_structs.stderr @@ -0,0 +1,77 @@ +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:7:22 + | +LL | flag: Option>, + | ^^^^^ help: use the applicable keyword: `Self` + | + = note: `-D clippy::use-self` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::use_self)]` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:17:22 + | +LL | flag: Option>>, + | ^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:38:22 + | +LL | flag: Option>>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:62:22 + | +LL | left: Option>, + | ^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:64:23 + | +LL | right: Option>, + | ^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:75:23 + | +LL | right: Option>, + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:80:33 + | +LL | flag: Option>>>, + | ^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:88:25 + | +LL | struct Tuple(Option>); + | ^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:97:46 + | +LL | flag: Vec>>>>>>, + | ^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:108:20 + | +LL | flag: Wrappers, + | ^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:117:23 + | +LL | flag: [Option>>; N], + | ^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self_structs.rs:127:14 + | +LL | Cons(Box), + | ^^^^ help: use the applicable keyword: `Self` + +error: aborting due to 12 previous errors + From 15575602da04d552f48be8d955daeaa91fb8bc04 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Fri, 10 Oct 2025 01:48:09 +0000 Subject: [PATCH 43/89] Remove StatementKind::Deinit. --- 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 1e3a7281bc734..b9027fea468eb 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -232,7 +232,7 @@ fn check_statement<'tcx>( StatementKind::FakeRead(box (_, place)) => check_place(cx, *place, span, body, msrv), // just an assignment - StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => { + StatementKind::SetDiscriminant { place, .. } => { check_place(cx, **place, span, body, msrv) }, From 6a08a85b065bba8060adbe9fd1ff2123b4ba3f7a Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Fri, 10 Oct 2025 18:16:58 +0200 Subject: [PATCH 44/89] Autolabel PR touching `declared_lints.rs` with `needs-fcp` --- triagebot.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index 57ff3dc352c8e..db951b95ef50a 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -45,6 +45,9 @@ reviewed_label = "S-waiting-on-author" [autolabel."S-waiting-on-review"] new_pr = true +[autolabel."needs-fcp"] +trigger_files = ["clippy_lints/src/declared_lints.rs"] + [concern] # These labels are set when there are unresolved concerns, removed otherwise labels = ["S-waiting-on-concerns"] From e0e5d478c12990667d2723bc4e84db9c46991bf3 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 3 Oct 2025 11:31:07 -0700 Subject: [PATCH 45/89] manual_unwrap_or: fix FP edge case --- clippy_lints/src/matches/manual_unwrap_or.rs | 12 +++++++----- tests/ui/manual_unwrap_or_default.fixed | 9 +++++++++ tests/ui/manual_unwrap_or_default.rs | 9 +++++++++ tests/ui/manual_unwrap_or_default.stderr | 4 ++-- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index 8c3f52542d917..d225bd35cb8be 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -31,7 +31,7 @@ fn get_some(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option { } } -fn get_none<'tcx>(cx: &LateContext<'_>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'tcx>> { +fn get_none<'tcx>(cx: &LateContext<'_>, arm: &Arm<'tcx>, allow_wildcard: bool) -> Option<&'tcx Expr<'tcx>> { if let PatKind::Expr(PatExpr { kind: PatExprKind::Path(QPath::Resolved(_, path)), .. }) = arm.pat.kind && let Some(def_id) = path.res.opt_def_id() // Since it comes from a pattern binding, we need to get the parent to actually match @@ -48,7 +48,9 @@ fn get_none<'tcx>(cx: &LateContext<'_>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'t && cx.tcx.lang_items().get(LangItem::ResultErr) == Some(def_id) { Some(arm.body) - } else if let PatKind::Wild = arm.pat.kind { + } else if let PatKind::Wild = arm.pat.kind + && allow_wildcard + { // We consider that the `Some` check will filter it out if it's not right. Some(arm.body) } else { @@ -62,11 +64,11 @@ fn get_some_and_none_bodies<'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) + && let Some(body_none) = get_none(cx, arm2, true) { 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) + } else if let Some(body_none) = get_none(cx, arm1, false) + && let Some(binding_id) = get_some(cx, arm2.pat) { Some(((arm2.body, binding_id), body_none)) } else { diff --git a/tests/ui/manual_unwrap_or_default.fixed b/tests/ui/manual_unwrap_or_default.fixed index 189fe876aa5d4..11023ac1142a7 100644 --- a/tests/ui/manual_unwrap_or_default.fixed +++ b/tests/ui/manual_unwrap_or_default.fixed @@ -32,6 +32,15 @@ fn main() { let x: Result = Ok(String::new()); x.unwrap_or_default(); + + // edge case + // because the `Some(bizarro)` pattern is not actually reachable, + // changing this match to `unwrap_or_default` would have side effects + let bizarro = Some(String::new()); + match bizarro { + _ => String::new(), + Some(bizarro) => bizarro, + }; } // Issue #12531 diff --git a/tests/ui/manual_unwrap_or_default.rs b/tests/ui/manual_unwrap_or_default.rs index ca87926763c9c..bf06d9af7d219 100644 --- a/tests/ui/manual_unwrap_or_default.rs +++ b/tests/ui/manual_unwrap_or_default.rs @@ -64,6 +64,15 @@ fn main() { } else { String::new() }; + + // edge case + // because the `Some(bizarro)` pattern is not actually reachable, + // changing this match to `unwrap_or_default` would have side effects + let bizarro = Some(String::new()); + match bizarro { + _ => String::new(), + Some(bizarro) => bizarro, + }; } // Issue #12531 diff --git a/tests/ui/manual_unwrap_or_default.stderr b/tests/ui/manual_unwrap_or_default.stderr index e8f38a2e3899e..031100832b16f 100644 --- a/tests/ui/manual_unwrap_or_default.stderr +++ b/tests/ui/manual_unwrap_or_default.stderr @@ -76,7 +76,7 @@ LL | | }; | |_____^ help: replace it with: `x.unwrap_or_default()` error: match can be simplified with `.unwrap_or_default()` - --> tests/ui/manual_unwrap_or_default.rs:74:24 + --> tests/ui/manual_unwrap_or_default.rs:83:24 | LL | Some(_) => match *b { | ________________________^ @@ -87,7 +87,7 @@ LL | | }, | |_____________^ help: replace it with: `(*b).unwrap_or_default()` error: if let can be simplified with `.unwrap_or_default()` - --> tests/ui/manual_unwrap_or_default.rs:143:5 + --> tests/ui/manual_unwrap_or_default.rs:152:5 | LL | / if let Some(x) = Some(42) { LL | | From 748a593a7f68dd8e3b1eceebc2dcb390d733f931 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 14 Sep 2025 04:48:17 -0400 Subject: [PATCH 46/89] Add new utils for defninition identification. --- book/src/development/adding_lints.md | 4 +- .../development/common_tools_writing_lints.md | 40 +- book/src/development/method_checking.md | 8 +- clippy_utils/src/lib.rs | 1 + clippy_utils/src/res.rs | 643 ++++++++++++++++++ 5 files changed, 670 insertions(+), 26 deletions(-) create mode 100644 clippy_utils/src/res.rs diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index 2b89e94cf8f4f..d9a5f04c3e1c0 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -759,8 +759,7 @@ for some users. Adding a configuration is done in the following steps: Here are some pointers to things you are likely going to need for every lint: * [Clippy utils][utils] - Various helper functions. Maybe the function you need - is already in here ([`is_type_diagnostic_item`], [`implements_trait`], - [`snippet`], etc) + is already in here ([`implements_trait`], [`snippet`], etc) * [Clippy diagnostics][diagnostics] * [Let chains][let-chains] * [`from_expansion`][from_expansion] and @@ -790,7 +789,6 @@ get away with copying things from existing similar lints. If you are stuck, don't hesitate to ask on [Zulip] or in the issue/PR. [utils]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/index.html -[`is_type_diagnostic_item`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html [`implements_trait`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html [`snippet`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html [let-chains]: https://github.com/rust-lang/rust/pull/94927 diff --git a/book/src/development/common_tools_writing_lints.md b/book/src/development/common_tools_writing_lints.md index 3bec3ce33af36..74fc788e9e3ae 100644 --- a/book/src/development/common_tools_writing_lints.md +++ b/book/src/development/common_tools_writing_lints.md @@ -85,8 +85,8 @@ to check for. All of these methods only check for the base type, generic arguments have to be checked separately. ```rust -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::paths; +use clippy_utils::res::MaybeDef; use rustc_span::symbol::sym; use rustc_hir::LangItem; @@ -97,12 +97,12 @@ impl LateLintPass<'_> for MyStructLint { // 1. Using diagnostic items // The last argument is the diagnostic item to check for - if is_type_diagnostic_item(cx, ty, sym::Option) { + if ty.is_diag_item(cx, sym::Option) { // The type is an `Option` } // 2. Using lang items - if is_type_lang_item(cx, ty, LangItem::RangeFull) { + if ty.is_lang_item(cx, LangItem::RangeFull) { // The type is a full range like `.drain(..)` } @@ -124,26 +124,28 @@ diagnostic item, lang item or neither. ```rust use clippy_utils::ty::implements_trait; -use clippy_utils::is_trait_method; use rustc_span::symbol::sym; impl LateLintPass<'_> for MyStructLint { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - // 1. Using diagnostic items with the expression - // we use `is_trait_method` function from Clippy's utils - if is_trait_method(cx, expr, sym::Iterator) { - // method call in `expr` belongs to `Iterator` trait - } - // 2. Using lang items with the expression type + // 1. Get the `DefId` of the trait. + // via lang items + let trait_id = cx.tcx.lang_items().drop_trait(); + // via diagnostic items + let trait_id = cx.tcx.get_diagnostic_item(sym::Eq); + + // 2. Check for the trait implementation via the `implements_trait` util. let ty = cx.typeck_results().expr_ty(expr); - if cx.tcx.lang_items() - // we are looking for the `DefId` of `Drop` trait in lang items - .drop_trait() - // then we use it with our type `ty` by calling `implements_trait` from Clippy's utils - .is_some_and(|id| implements_trait(cx, ty, id, &[])) { - // `expr` implements `Drop` trait - } + if trait_id.is_some_and(|id| implements_trait(cx, ty, id, &[])) { + // `ty` implements the trait. + } + + // 3. If the trait requires additional generic arguments + let trait_id = cx.tcx.lang_items().eq_trait(); + if trait_id.is_some_and(|id| implements_trait(cx, ty, id, &[ty])) { + // `ty` implements `PartialEq` + } } } ``` @@ -159,7 +161,7 @@ paths for Clippy can be found in [paths.rs][paths] To check if our type defines a method called `some_method`: ```rust -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use clippy_utils::{sym, return_ty}; impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { @@ -173,7 +175,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { // We can also check it has a parameter `self` && signature.decl.implicit_self.has_implicit_self() // We can go further and even check if its return type is `String` - && is_type_lang_item(cx, return_ty(cx, impl_item.hir_id), LangItem::String) + && return_ty(cx, impl_item.hir_id).is_lang_item(cx, LangItem::String) { // ... } diff --git a/book/src/development/method_checking.md b/book/src/development/method_checking.md index b3126024b990d..7819a477f608c 100644 --- a/book/src/development/method_checking.md +++ b/book/src/development/method_checking.md @@ -16,7 +16,7 @@ the [`ExprKind`] that we can access from `expr.kind`: use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_span::sym; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { @@ -28,7 +28,7 @@ impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint { // (It's necessary if we want to check that method is specifically belonging to a specific trait, // for example, a `map` method could belong to user-defined trait instead of to `Iterator`) // See the next section for more information. - && is_trait_method(cx, self_arg, sym::OurFancyTrait) + && cx.ty_based_def(self_arg).opt_parent(cx).is_diag_item(cx, sym::OurFancyTrait) { println!("`expr` is a method call for `our_fancy_method`"); } @@ -56,7 +56,7 @@ Let us take a look at how we might check for the implementation of `our_fancy_method` on a type: ```rust -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::return_ty; use rustc_hir::{ImplItem, ImplItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -71,7 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { // We can also check it has a parameter `self` && signature.decl.implicit_self.has_implicit_self() // We can go even further and even check if its return type is `String` - && is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::String) + && return_ty(cx, impl_item.hir_id).is_diag_item(cx, sym::String) { println!("`our_fancy_method` is implemented!"); } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 9ae3663057720..a54d32268bf84 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -66,6 +66,7 @@ pub mod msrvs; pub mod numeric_literal; pub mod paths; pub mod qualify_min_const_fn; +pub mod res; pub mod source; pub mod str_utils; pub mod sugg; diff --git a/clippy_utils/src/res.rs b/clippy_utils/src/res.rs new file mode 100644 index 0000000000000..90b9529278964 --- /dev/null +++ b/clippy_utils/src/res.rs @@ -0,0 +1,643 @@ +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{ + self as hir, Expr, ExprKind, HirId, LangItem, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, TyKind, +}; +use rustc_lint::LateContext; +use rustc_middle::ty::layout::HasTyCtxt; +use rustc_middle::ty::{AdtDef, AdtKind, Binder, EarlyBinder, Ty, TypeckResults}; +use rustc_span::{Ident, Symbol}; + +/// Either a `HirId` or a type which can be identified by one. +pub trait HasHirId: Copy { + fn hir_id(self) -> HirId; +} +impl HasHirId for HirId { + #[inline] + fn hir_id(self) -> HirId { + self + } +} +impl HasHirId for &Expr<'_> { + #[inline] + fn hir_id(self) -> HirId { + self.hir_id + } +} + +type DefRes = (DefKind, DefId); + +pub trait MaybeTypeckRes<'tcx> { + /// Gets the contained `TypeckResults`. + /// + /// With debug assertions enabled this will always return `Some`. `None` is + /// only returned so logic errors can be handled by not emitting a lint on + /// release builds. + fn typeck_res(&self) -> Option<&TypeckResults<'tcx>>; + + /// Gets the type-dependent resolution of the specified node. + /// + /// With debug assertions enabled this will always return `Some`. `None` is + /// only returned so logic errors can be handled by not emitting a lint on + /// release builds. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn ty_based_def(&self, node: impl HasHirId) -> Option { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn f(typeck: &TypeckResults<'_>, id: HirId) -> Option { + if typeck.hir_owner == id.owner { + let def = typeck.type_dependent_def(id); + debug_assert!( + def.is_some(), + "attempted type-dependent lookup for a node with no definition\ + \n node `{id:?}`", + ); + def + } else { + debug_assert!( + false, + "attempted type-dependent lookup for a node in the wrong body\ + \n in body `{:?}`\ + \n expected body `{:?}`", + typeck.hir_owner, id.owner, + ); + None + } + } + self.typeck_res().and_then(|typeck| f(typeck, node.hir_id())) + } +} +impl<'tcx> MaybeTypeckRes<'tcx> for LateContext<'tcx> { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn typeck_res(&self) -> Option<&TypeckResults<'tcx>> { + if let Some(typeck) = self.maybe_typeck_results() { + Some(typeck) + } else { + // It's possible to get the `TypeckResults` for any other body, but + // attempting to lookup the type of something across bodies like this + // is a good indication of a bug. + debug_assert!(false, "attempted type-dependent lookup in a non-body context"); + None + } + } +} +impl<'tcx> MaybeTypeckRes<'tcx> for TypeckResults<'tcx> { + #[inline] + fn typeck_res(&self) -> Option<&TypeckResults<'tcx>> { + Some(self) + } +} + +/// A `QPath` with the `HirId` of the node containing it. +type QPathId<'tcx> = (&'tcx QPath<'tcx>, HirId); + +/// A HIR node which might be a `QPath`. +pub trait MaybeQPath<'a>: Copy { + /// If this node is a path gets both the contained path and the `HirId` to + /// use for type dependant lookup. + fn opt_qpath(self) -> Option>; + + /// If this node is a `QPath::LangItem` gets the item it resolves to. + #[inline] + fn opt_lang_path(self) -> Option { + match self.opt_qpath() { + Some((&QPath::LangItem(item, _), _)) => Some(item), + _ => None, + } + } + + /// If this is a path gets its resolution. Returns `Res::Err` otherwise. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn res<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> Res { + #[cfg_attr(debug_assertions, track_caller)] + fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>) -> Res { + match *qpath { + QPath::Resolved(_, p) => p.res, + QPath::TypeRelative(..) | QPath::LangItem(..) if let Some((kind, id)) = typeck.ty_based_def(id) => { + Res::Def(kind, id) + }, + QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err, + } + } + match self.opt_qpath() { + Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck), + _ => Res::Err, + } + } + + /// If this is a path with the specified name as its final segment gets its + /// resolution. Returns `Res::Err` otherwise. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn res_if_named<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>, name: Symbol) -> Res { + #[cfg_attr(debug_assertions, track_caller)] + fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>, name: Symbol) -> Res { + match *qpath { + QPath::Resolved(_, p) + if let [.., seg] = p.segments + && seg.ident.name == name => + { + p.res + }, + QPath::TypeRelative(_, seg) + if seg.ident.name == name + && let Some((kind, id)) = typeck.ty_based_def(id) => + { + Res::Def(kind, id) + }, + QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err, + } + } + match self.opt_qpath() { + Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck, name), + _ => Res::Err, + } + } + + /// If this is a path gets both its resolution and final segment. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn res_with_seg<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> (Res, Option<&'a PathSegment<'a>>) { + #[cfg_attr(debug_assertions, track_caller)] + fn f<'a>(qpath: &QPath<'a>, id: HirId, typeck: &TypeckResults<'_>) -> (Res, Option<&'a PathSegment<'a>>) { + match *qpath { + QPath::Resolved(_, p) if let [.., seg] = p.segments => (p.res, Some(seg)), + QPath::TypeRelative(_, seg) if let Some((kind, id)) = typeck.ty_based_def(id) => { + (Res::Def(kind, id), Some(seg)) + }, + QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => (Res::Err, None), + } + } + match self.opt_qpath() { + Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck), + _ => (Res::Err, None), + } + } + + /// If this is a path without an explicit `Self` type gets its resolution. + /// Returns `Res::Err` otherwise. + /// + /// Only paths to trait items can optionally contain a `Self` type. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn typeless_res<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> Res { + #[cfg_attr(debug_assertions, track_caller)] + fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>) -> Res { + match *qpath { + QPath::Resolved( + None + | Some(&hir::Ty { + kind: TyKind::Infer(()), + .. + }), + p, + ) => p.res, + QPath::TypeRelative( + &hir::Ty { + kind: TyKind::Infer(()), + .. + }, + _, + ) if let Some((kind, id)) = typeck.ty_based_def(id) => Res::Def(kind, id), + QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err, + } + } + match self.opt_qpath() { + Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck), + _ => Res::Err, + } + } + + /// If this is a path without an explicit `Self` type to an item with the + /// specified name gets its resolution. Returns `Res::Err` otherwise. + /// + /// Only paths to trait items can optionally contain a `Self` type. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn typeless_res_if_named<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>, name: Symbol) -> Res { + #[cfg_attr(debug_assertions, track_caller)] + fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>, name: Symbol) -> Res { + match *qpath { + QPath::Resolved( + None + | Some(&hir::Ty { + kind: TyKind::Infer(()), + .. + }), + p, + ) if let [.., seg] = p.segments + && seg.ident.name == name => + { + p.res + }, + QPath::TypeRelative( + &hir::Ty { + kind: TyKind::Infer(()), + .. + }, + seg, + ) if seg.ident.name == name + && let Some((kind, id)) = typeck.ty_based_def(id) => + { + Res::Def(kind, id) + }, + QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err, + } + } + match self.opt_qpath() { + Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck, name), + _ => Res::Err, + } + } + + /// If this is a type-relative path gets the definition it resolves to. + /// + /// Only inherent associated items require a type-relative path. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn ty_rel_def<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> Option { + match self.opt_qpath() { + Some((QPath::TypeRelative(..), id)) => typeck.ty_based_def(id), + _ => None, + } + } + + /// If this is a type-relative path to an item with the specified name gets + /// the definition it resolves to. + /// + /// Only inherent associated items require a type-relative path. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn ty_rel_def_if_named<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>, name: Symbol) -> Option { + match self.opt_qpath() { + Some((&QPath::TypeRelative(_, seg), id)) if seg.ident.name == name => typeck.ty_based_def(id), + _ => None, + } + } + + /// If this is a type-relative path gets the definition it resolves to and + /// its final segment. + /// + /// Only inherent associated items require a type-relative path. + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + fn ty_rel_def_with_seg<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> Option<(DefRes, &'a PathSegment<'a>)> { + match self.opt_qpath() { + Some((QPath::TypeRelative(_, seg), id)) if let Some(def) = typeck.ty_based_def(id) => Some((def, seg)), + _ => None, + } + } +} + +impl<'tcx> MaybeQPath<'tcx> for QPathId<'tcx> { + #[inline] + fn opt_qpath(self) -> Option> { + Some((self.0, self.1)) + } +} +impl<'tcx> MaybeQPath<'tcx> for &'tcx Expr<'_> { + #[inline] + fn opt_qpath(self) -> Option> { + match &self.kind { + ExprKind::Path(qpath) => Some((qpath, self.hir_id)), + _ => None, + } + } +} +impl<'tcx> MaybeQPath<'tcx> for &'tcx PatExpr<'_> { + #[inline] + fn opt_qpath(self) -> Option> { + match &self.kind { + PatExprKind::Path(qpath) => Some((qpath, self.hir_id)), + _ => None, + } + } +} +impl<'tcx, AmbigArg> MaybeQPath<'tcx> for &'tcx hir::Ty<'_, AmbigArg> { + #[inline] + fn opt_qpath(self) -> Option> { + match &self.kind { + TyKind::Path(qpath) => Some((qpath, self.hir_id)), + _ => None, + } + } +} +impl<'tcx> MaybeQPath<'tcx> for &'_ Pat<'tcx> { + #[inline] + fn opt_qpath(self) -> Option> { + match self.kind { + PatKind::Expr(e) => e.opt_qpath(), + _ => None, + } + } +} +impl<'tcx, T: MaybeQPath<'tcx>> MaybeQPath<'tcx> for Option { + #[inline] + fn opt_qpath(self) -> Option> { + self.and_then(T::opt_qpath) + } +} +impl<'tcx, T: Copy + MaybeQPath<'tcx>> MaybeQPath<'tcx> for &Option { + #[inline] + fn opt_qpath(self) -> Option> { + self.and_then(T::opt_qpath) + } +} + +/// A resolved path and the explicit `Self` type if there is one. +type OptResPath<'tcx> = (Option<&'tcx hir::Ty<'tcx>>, Option<&'tcx Path<'tcx>>); + +/// A HIR node which might be a `QPath::Resolved`. +/// +/// The following are resolved paths: +/// * A path to a module or crate item. +/// * A path to a trait item via the trait's name. +/// * A path to a struct or variant constructor via the original type's path. +/// * A local. +/// +/// All other paths are `TypeRelative` and require using `PathRes` to lookup the +/// resolution. +pub trait MaybeResPath<'a>: Copy { + /// If this node is a resolved path gets both the contained path and the + /// type associated with it. + fn opt_res_path(self) -> OptResPath<'a>; + + /// If this node is a resolved path gets it's resolution. Returns `Res::Err` + /// otherwise. + #[inline] + fn basic_res(self) -> &'a Res { + self.opt_res_path().1.map_or(&Res::Err, |p| &p.res) + } + + /// If this node is a path to a local gets the local's `HirId`. + #[inline] + fn res_local_id(self) -> Option { + if let (_, Some(p)) = self.opt_res_path() + && let Res::Local(id) = p.res + { + Some(id) + } else { + None + } + } + + /// If this node is a path to a local gets the local's `HirId` and identifier. + fn res_local_id_and_ident(self) -> Option<(HirId, &'a Ident)> { + if let (_, Some(p)) = self.opt_res_path() + && let Res::Local(id) = p.res + && let [seg] = p.segments + { + Some((id, &seg.ident)) + } else { + None + } + } +} +impl<'a> MaybeResPath<'a> for &'a Path<'a> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + (None, Some(self)) + } + + #[inline] + fn basic_res(self) -> &'a Res { + &self.res + } +} +impl<'a> MaybeResPath<'a> for &QPath<'a> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match *self { + QPath::Resolved(ty, path) => (ty, Some(path)), + _ => (None, None), + } + } +} +impl<'a> MaybeResPath<'a> for &Expr<'a> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match &self.kind { + ExprKind::Path(qpath) => qpath.opt_res_path(), + _ => (None, None), + } + } +} +impl<'a> MaybeResPath<'a> for &PatExpr<'a> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match &self.kind { + PatExprKind::Path(qpath) => qpath.opt_res_path(), + _ => (None, None), + } + } +} +impl<'a, AmbigArg> MaybeResPath<'a> for &hir::Ty<'a, AmbigArg> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match &self.kind { + TyKind::Path(qpath) => qpath.opt_res_path(), + _ => (None, None), + } + } +} +impl<'a> MaybeResPath<'a> for &Pat<'a> { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match self.kind { + PatKind::Expr(e) => e.opt_res_path(), + _ => (None, None), + } + } +} +impl<'a, T: MaybeResPath<'a>> MaybeResPath<'a> for Option { + #[inline] + fn opt_res_path(self) -> OptResPath<'a> { + match self { + Some(x) => T::opt_res_path(x), + None => (None, None), + } + } + + #[inline] + fn basic_res(self) -> &'a Res { + self.map_or(&Res::Err, T::basic_res) + } +} + +/// A type which may either contain a `DefId` or be referred to by a `DefId`. +pub trait MaybeDef: Copy { + fn opt_def_id(self) -> Option; + + /// Gets this definition's id and kind. This will lookup the kind in the def + /// tree if needed. + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)>; + + /// Gets the diagnostic name of this definition if it has one. + #[inline] + fn opt_diag_name<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option { + self.opt_def_id().and_then(|id| tcx.tcx().get_diagnostic_name(id)) + } + + /// Checks if this definition has the specified diagnostic name. + #[inline] + fn is_diag_item<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>, name: Symbol) -> bool { + self.opt_def_id() + .is_some_and(|id| tcx.tcx().is_diagnostic_item(name, id)) + } + + /// Checks if this definition is the specified `LangItem`. + #[inline] + fn is_lang_item<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>, item: LangItem) -> bool { + self.opt_def_id() + .is_some_and(|id| tcx.tcx().lang_items().get(item) == Some(id)) + } + + /// If this definition is an impl block gets its type. + #[inline] + fn opt_impl_ty<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option>> { + match self.opt_def(tcx) { + Some((DefKind::Impl { .. }, id)) => Some(tcx.tcx().type_of(id)), + _ => None, + } + } + + /// Gets the parent of this definition if it has one. + #[inline] + fn opt_parent<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option { + self.opt_def_id().and_then(|id| tcx.tcx().opt_parent(id)) + } + + /// Checks if this definition is an impl block. + #[inline] + fn is_impl<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> bool { + matches!(self.opt_def(tcx), Some((DefKind::Impl { .. }, _))) + } + + /// If this definition is a constructor gets the `DefId` of it's type or variant. + #[inline] + fn ctor_parent<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option { + match self.opt_def(tcx) { + Some((DefKind::Ctor(..), id)) => tcx.tcx().opt_parent(id), + _ => None, + } + } + + /// If this definition is an associated item of an impl or trait gets the + /// `DefId` of its parent. + #[inline] + fn assoc_parent<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option { + match self.opt_def(tcx) { + Some((DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, id)) => tcx.tcx().opt_parent(id), + _ => None, + } + } + + /// If this definition is an associated function of an impl or trait gets the + /// `DefId` of its parent. + #[inline] + fn assoc_fn_parent<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option { + match self.opt_def(tcx) { + Some((DefKind::AssocFn, id)) => tcx.tcx().opt_parent(id), + _ => None, + } + } +} +impl MaybeDef for DefId { + #[inline] + fn opt_def_id(self) -> Option { + Some(self) + } + + #[inline] + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + self.opt_def_id().map(|id| (tcx.tcx().def_kind(id), id)) + } +} +impl MaybeDef for (DefKind, DefId) { + #[inline] + fn opt_def_id(self) -> Option { + Some(self.1) + } + + #[inline] + fn opt_def<'tcx>(self, _: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + Some(self) + } +} +impl MaybeDef for AdtDef<'_> { + #[inline] + fn opt_def_id(self) -> Option { + Some(self.did()) + } + + #[inline] + fn opt_def<'tcx>(self, _: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + let did = self.did(); + match self.adt_kind() { + AdtKind::Enum => Some((DefKind::Enum, did)), + AdtKind::Struct => Some((DefKind::Struct, did)), + AdtKind::Union => Some((DefKind::Union, did)), + } + } +} +impl MaybeDef for Ty<'_> { + #[inline] + fn opt_def_id(self) -> Option { + self.ty_adt_def().opt_def_id() + } + + #[inline] + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + self.ty_adt_def().opt_def(tcx) + } +} +impl MaybeDef for Res { + #[inline] + fn opt_def_id(self) -> Option { + Res::opt_def_id(&self) + } + + #[inline] + fn opt_def<'tcx>(self, _: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + match self { + Res::Def(kind, id) => Some((kind, id)), + _ => None, + } + } +} +impl MaybeDef for Option { + #[inline] + fn opt_def_id(self) -> Option { + self.and_then(T::opt_def_id) + } + + #[inline] + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + self.and_then(|x| T::opt_def(x, tcx)) + } +} +impl MaybeDef for EarlyBinder<'_, T> { + #[inline] + fn opt_def_id(self) -> Option { + self.skip_binder().opt_def_id() + } + + #[inline] + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + self.skip_binder().opt_def(tcx) + } +} +impl MaybeDef for Binder<'_, T> { + #[inline] + fn opt_def_id(self) -> Option { + self.skip_binder().opt_def_id() + } + + #[inline] + fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> { + self.skip_binder().opt_def(tcx) + } +} From d32ef64ed521df8307f0503111a195b01ffcbac1 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 04:09:27 -0400 Subject: [PATCH 47/89] Remove `is_path_lang_item` --- clippy_lints/src/missing_fields_in_debug.rs | 7 +++++-- clippy_lints/src/pub_underscore_fields.rs | 4 ++-- clippy_lints/src/question_mark.rs | 17 +++++++++-------- clippy_lints/src/ranges.rs | 6 +++--- clippy_lints/src/types/owned_cow.rs | 3 ++- clippy_lints/src/utils/author.rs | 2 +- clippy_utils/src/lib.rs | 9 ++------- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs index 8822b32b1c3d6..1b5671b8d0164 100644 --- a/clippy_lints/src/missing_fields_in_debug.rs +++ b/clippy_lints/src/missing_fields_in_debug.rs @@ -1,9 +1,10 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::sym; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::{Visitable, for_each_expr}; -use clippy_utils::{is_path_lang_item, sym}; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; @@ -180,7 +181,9 @@ fn check_struct<'tcx>( .fields() .iter() .filter_map(|field| { - if field_accesses.contains(&field.ident.name) || is_path_lang_item(cx, field.ty, LangItem::PhantomData) { + if field_accesses.contains(&field.ident.name) + || field.ty.basic_res().is_lang_item(cx, LangItem::PhantomData) + { None } else { Some((field.span, "this field is unused")) diff --git a/clippy_lints/src/pub_underscore_fields.rs b/clippy_lints/src/pub_underscore_fields.rs index 66c59cb70d361..694d44d708568 100644 --- a/clippy_lints/src/pub_underscore_fields.rs +++ b/clippy_lints/src/pub_underscore_fields.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_config::types::PubUnderscoreFieldsBehaviour; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::is_path_lang_item; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::{FieldDef, Item, ItemKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -76,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields { // We ignore fields that have `#[doc(hidden)]`. && !is_doc_hidden(cx.tcx.hir_attrs(field.hir_id)) // We ignore fields that are `PhantomData`. - && !is_path_lang_item(cx, field.ty, LangItem::PhantomData) + && !field.ty.basic_res().is_lang_item(cx, LangItem::PhantomData) { span_lint_hir_and_then( cx, diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index d3a5a5dddfbeb..49c58a276cdd9 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -4,14 +4,15 @@ use clippy_config::Conf; use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{ eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, - is_path_lang_item, is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, - peel_blocks, peel_blocks_with_stmt, span_contains_cfg, span_contains_comment, sym, + is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, peel_blocks, + peel_blocks_with_stmt, span_contains_cfg, span_contains_comment, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; @@ -521,11 +522,11 @@ impl QuestionMark { } } -fn is_try_block(cx: &LateContext<'_>, bl: &Block<'_>) -> bool { +fn is_try_block(bl: &Block<'_>) -> bool { if let Some(expr) = bl.expr && let ExprKind::Call(callee, [_]) = expr.kind { - is_path_lang_item(cx, callee, LangItem::TryTraitFromOutput) + callee.opt_lang_path() == Some(LangItem::TryTraitFromOutput) } else { false } @@ -581,8 +582,8 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { } } - fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if is_try_block(cx, block) { + fn check_block(&mut self, _: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + if is_try_block(block) { *self .try_block_depth_stack .last_mut() @@ -598,8 +599,8 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { self.try_block_depth_stack.pop(); } - fn check_block_post(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if is_try_block(cx, block) { + fn check_block_post(&mut self, _: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + if is_try_block(block) { *self .try_block_depth_stack .last_mut() diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 0b2313cb7eeb9..945b67f821f6e 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -2,12 +2,12 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::{ - expr_use_ctxt, fn_def_id, get_parent_expr, higher, is_in_const_context, is_integer_const, is_path_lang_item, - path_to_local, + expr_use_ctxt, fn_def_id, get_parent_expr, higher, is_in_const_context, is_integer_const, path_to_local, }; use rustc_ast::Mutability; use rustc_ast::ast::RangeLimits; @@ -370,7 +370,7 @@ fn can_switch_ranges<'tcx>( // Check if `expr` is the argument of a compiler-generated `IntoIter::into_iter(expr)` if let ExprKind::Call(func, [arg]) = parent_expr.kind && arg.hir_id == use_ctxt.child_id - && is_path_lang_item(cx, func, LangItem::IntoIterIntoIter) + && func.opt_lang_path() == Some(LangItem::IntoIterIntoIter) { return true; } diff --git a/clippy_lints/src/types/owned_cow.rs b/clippy_lints/src/types/owned_cow.rs index 8933994d18558..9d4d885bf6cd6 100644 --- a/clippy_lints/src/types/owned_cow.rs +++ b/clippy_lints/src/types/owned_cow.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_opt; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -30,7 +31,7 @@ pub(super) fn check(cx: &LateContext<'_>, qpath: &hir::QPath<'_>, def_id: DefId) } fn replacement(cx: &LateContext<'_>, cty: &hir::Ty<'_>) -> Option<(Span, String)> { - if clippy_utils::is_path_lang_item(cx, cty, hir::LangItem::String) { + if cty.basic_res().is_lang_item(cx, hir::LangItem::String) { return Some((cty.span, "str".into())); } if clippy_utils::is_path_diagnostic_item(cx, cty, sym::Vec) { diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 08210ae2cefb9..f25387b5e66cc 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -296,7 +296,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { && !id.is_local() { if let Some(lang) = self.cx.tcx.lang_items().from_def_id(id) { - chain!(self, "is_path_lang_item(cx, {path}, LangItem::{}", lang.name()); + chain!(self, "{path}.res(cx).is_lang_item(cx, LangItem::{}", lang.name()); } else if let Some(name) = self.cx.tcx.get_diagnostic_name(id) { chain!(self, "is_path_diagnostic_item(cx, {path}, sym::{name})"); } else { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index a54d32268bf84..e92373941dc11 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -132,6 +132,7 @@ use crate::ast_utils::unordered_over; use crate::consts::{ConstEvalCtxt, Constant}; use crate::higher::Range; use crate::msrvs::Msrv; +use crate::res::{MaybeDef, MaybeResPath}; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -437,12 +438,6 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool { - path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.lang_items().get(lang_item) == Some(id)) -} - /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if /// it matches the given diagnostic item. pub fn is_path_diagnostic_item<'tcx>( @@ -788,7 +783,7 @@ fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: & ExprKind::Lit(hir::Lit { node: LitKind::Str(sym, _), .. - }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String), + }) => return sym.is_empty() && ty.basic_res().is_lang_item(cx, LangItem::String), ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec), ExprKind::Repeat(_, len) => { if let ConstArgKind::Anon(anon_const) = len.kind From cb32444ee6de2b2cde9b495fa9f69f5f192b68eb Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 05:27:27 -0400 Subject: [PATCH 48/89] Remove `is_path_diagnostic_item` --- clippy_lints/src/casts/manual_dangling_ptr.rs | 5 +++-- clippy_lints/src/manual_option_as_slice.rs | 7 ++++--- .../src/methods/from_iter_instead_of_collect.rs | 5 +++-- clippy_lints/src/methods/io_other_error.rs | 5 +++-- clippy_lints/src/methods/manual_str_repeat.rs | 4 ++-- .../src/methods/needless_character_iteration.rs | 5 +++-- clippy_lints/src/methods/uninit_assumed_init.rs | 4 ++-- clippy_lints/src/slow_vector_initialization.rs | 10 +++++----- clippy_lints/src/time_subtraction.rs | 5 +++-- clippy_lints/src/to_digit_is_some.rs | 5 +++-- clippy_lints/src/transmute/transmute_null_to_fn.rs | 5 +++-- clippy_lints/src/transmute/transmuting_null.rs | 5 +++-- clippy_lints/src/types/owned_cow.rs | 6 +++--- clippy_lints/src/utils/author.rs | 2 +- clippy_utils/src/lib.rs | 14 ++------------ tests/ui/author/blocks.stdout | 4 ++-- tests/ui/author/call.stdout | 2 +- tests/ui/author/issue_3849.stdout | 2 +- 18 files changed, 47 insertions(+), 48 deletions(-) diff --git a/clippy_lints/src/casts/manual_dangling_ptr.rs b/clippy_lints/src/casts/manual_dangling_ptr.rs index ff5320719aa28..be1f406770ce6 100644 --- a/clippy_lints/src/casts/manual_dangling_ptr.rs +++ b/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{expr_or_init, is_path_diagnostic_item, std_or_core, sym}; +use clippy_utils::{expr_or_init, std_or_core, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind}; @@ -53,7 +54,7 @@ fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) -> fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, to: &Ty<'_>) -> bool { if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind - && is_path_diagnostic_item(cx, fun, sym::mem_align_of) + && fun.basic_res().is_diag_item(cx, sym::mem_align_of) && let Some(args) = path.segments.last().and_then(|seg| seg.args) && let [GenericArg::Type(generic_ty)] = args.args { diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs index b036e78cdedc5..63f6d89f2ad74 100644 --- a/clippy_lints/src/manual_option_as_slice.rs +++ b/clippy_lints/src/manual_option_as_slice.rs @@ -1,6 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::Msrv; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::{is_none_pattern, msrvs, peel_hir_expr_refs, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -189,7 +190,7 @@ fn check_arms(cx: &LateContext<'_>, none_arm: &Arm<'_>, some_arm: &Arm<'_>) -> b fn returns_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { - ExprKind::Path(_) => clippy_utils::is_path_diagnostic_item(cx, expr, sym::default_fn), + ExprKind::Path(_) => expr.res(cx).is_diag_item(cx, sym::default_fn), ExprKind::Closure(cl) => is_empty_slice(cx, cx.tcx.hir_body(cl.body).value), _ => false, } @@ -214,11 +215,11 @@ fn is_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _ => false, }, ExprKind::Array([]) => true, - ExprKind::Call(def, []) => clippy_utils::is_path_diagnostic_item(cx, def, sym::default_fn), + ExprKind::Call(def, []) => def.res(cx).is_diag_item(cx, sym::default_fn), _ => false, } } fn is_slice_from_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - clippy_utils::is_path_diagnostic_item(cx, expr, sym::slice_from_ref) + expr.basic_res().is_diag_item(cx, sym::slice_from_ref) } diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index d664eaaac704f..11671f377e668 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -1,9 +1,10 @@ use std::fmt::Write as _; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_path_diagnostic_item, sugg}; use rustc_ast::join_path_idents; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -15,7 +16,7 @@ use rustc_span::sym; use super::FROM_ITER_INSTEAD_OF_COLLECT; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>], func: &Expr<'_>) { - if is_path_diagnostic_item(cx, func, sym::from_iter_fn) + if func.res(cx).is_diag_item(cx, sym::from_iter_fn) && let arg_ty = cx.typeck_results().expr_ty(&args[0]) && let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator) && implements_trait(cx, arg_ty, iter_id, &[]) diff --git a/clippy_lints/src/methods/io_other_error.rs b/clippy_lints/src/methods/io_other_error.rs index 9276261606e1e..b081e804859a5 100644 --- a/clippy_lints/src/methods/io_other_error.rs +++ b/clippy_lints/src/methods/io_other_error.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::{expr_or_init, is_path_diagnostic_item, sym}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::{expr_or_init, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; @@ -10,7 +11,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, path: &Expr<'_>, args && !expr.span.from_expansion() && !error_kind.span.from_expansion() && let ExprKind::Path(QPath::TypeRelative(_, new_segment)) = path.kind - && is_path_diagnostic_item(cx, path, sym::io_error_new) + && path.ty_rel_def(cx).is_diag_item(cx, sym::io_error_new) && let ExprKind::Path(QPath::Resolved(_, init_path)) = &expr_or_init(cx, error_kind).kind && let [.., error_kind_ty, error_kind_variant] = init_path.segments && cx.tcx.is_diagnostic_item(sym::io_errorkind, error_kind_ty.res.def_id()) diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index a811dd1cee18f..7d6f56e4b31b6 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_path_diagnostic_item; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; @@ -55,7 +55,7 @@ pub(super) fn check( take_arg: &Expr<'_>, ) { if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind - && is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat) + && repeat_fn.basic_res().is_diag_item(cx, sym::iter_repeat) && is_type_lang_item(cx, cx.typeck_results().expr_ty(collect_expr), LangItem::String) && let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id) && let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator) diff --git a/clippy_lints/src/methods/needless_character_iteration.rs b/clippy_lints/src/methods/needless_character_iteration.rs index 71c1576cd57d1..d161c002d083f 100644 --- a/clippy_lints/src/methods/needless_character_iteration.rs +++ b/clippy_lints/src/methods/needless_character_iteration.rs @@ -1,3 +1,4 @@ +use clippy_utils::res::{MaybeDef, MaybeQPath}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, HirId, StmtKind, UnOp}; use rustc_lint::LateContext; @@ -8,7 +9,7 @@ use super::NEEDLESS_CHARACTER_ITERATION; use super::utils::get_last_chain_binding_hir_id; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{is_path_diagnostic_item, path_to_local_id, peel_blocks, sym}; +use clippy_utils::{path_to_local_id, peel_blocks, sym}; fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> { while let ExprKind::AddrOf(_, _, e) = expr.kind { @@ -75,7 +76,7 @@ fn handle_expr( // If we have `!is_ascii`, then only `.any()` should warn. And if the condition is // `is_ascii`, then only `.all()` should warn. if revert != is_all - && is_path_diagnostic_item(cx, fn_path, sym::char_is_ascii) + && fn_path.ty_rel_def(cx).is_diag_item(cx, sym::char_is_ascii) && path_to_local_id(peels_expr_ref(arg), first_param) && let Some(snippet) = before_chars.get_source_text(cx) { diff --git a/clippy_lints/src/methods/uninit_assumed_init.rs b/clippy_lints/src/methods/uninit_assumed_init.rs index 6371fe6442826..5e247a50358e6 100644 --- a/clippy_lints/src/methods/uninit_assumed_init.rs +++ b/clippy_lints/src/methods/uninit_assumed_init.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_path_diagnostic_item; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::ty::is_uninit_value_valid_for_ty; use rustc_hir as hir; use rustc_lint::LateContext; @@ -10,7 +10,7 @@ use super::UNINIT_ASSUMED_INIT; /// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter) pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { if let hir::ExprKind::Call(callee, []) = recv.kind - && is_path_diagnostic_item(cx, callee, sym::maybe_uninit_uninit) + && callee.ty_rel_def(cx).is_diag_item(cx, sym::maybe_uninit_uninit) && !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr)) { span_lint( diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index f497d0700b8eb..237c8e66ee3fc 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::matching_root_macro_call; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - SpanlessEq, get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, - span_contains_comment, sym, + SpanlessEq, get_enclosing_block, is_integer_literal, path_to_local, path_to_local_id, span_contains_comment, sym, }; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; @@ -149,10 +149,10 @@ impl SlowVectorInit { } if let ExprKind::Call(func, [len_expr]) = expr.kind - && is_path_diagnostic_item(cx, func, sym::vec_with_capacity) + && func.ty_rel_def(cx).is_diag_item(cx, sym::vec_with_capacity) { Some(InitializedSize::Initialized(len_expr)) - } else if matches!(expr.kind, ExprKind::Call(func, []) if is_path_diagnostic_item(cx, func, sym::vec_new)) { + } else if matches!(expr.kind, ExprKind::Call(func, []) if func.ty_rel_def(cx).is_diag_item(cx, sym::vec_new)) { Some(InitializedSize::Uninitialized) } else { None @@ -301,7 +301,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { /// Returns `true` if given expression is `repeat(0)` fn is_repeat_zero(&self, expr: &Expr<'_>) -> bool { if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind - && is_path_diagnostic_item(self.cx, fn_expr, sym::iter_repeat) + && fn_expr.basic_res().is_diag_item(self.cx, sym::iter_repeat) && is_integer_literal(repeat_arg, 0) { true diff --git a/clippy_lints/src/time_subtraction.rs b/clippy_lints/src/time_subtraction.rs index fde8c3d9a9a76..c25668817f8ba 100644 --- a/clippy_lints/src/time_subtraction.rs +++ b/clippy_lints/src/time_subtraction.rs @@ -1,8 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_path_diagnostic_item, ty}; +use clippy_utils::ty; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -146,7 +147,7 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { if let ExprKind::Call(fn_expr, []) = expr_block.kind - && is_path_diagnostic_item(cx, fn_expr, sym::instant_now) + && cx.ty_based_def(fn_expr).is_diag_item(cx, sym::instant_now) { true } else { diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs index 3e847543e1c16..7b90f20b6ba3c 100644 --- a/clippy_lints/src/to_digit_is_some.rs +++ b/clippy_lints/src/to_digit_is_some.rs @@ -1,8 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_in_const_context, is_path_diagnostic_item, sym}; +use clippy_utils::{is_in_const_context, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -62,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { } }, hir::ExprKind::Call(to_digits_call, [char_arg, radix_arg]) => { - if is_path_diagnostic_item(cx, to_digits_call, sym::char_to_digit) { + if to_digits_call.ty_rel_def(cx).is_diag_item(cx, sym::char_to_digit) { Some((false, char_arg, radix_arg)) } else { None diff --git a/clippy_lints/src/transmute/transmute_null_to_fn.rs b/clippy_lints/src/transmute/transmute_null_to_fn.rs index 7acf3be51fb7e..e109b1c50e227 100644 --- a/clippy_lints/src/transmute/transmute_null_to_fn.rs +++ b/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -1,6 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; +use clippy_utils::is_integer_literal; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; @@ -40,7 +41,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t }, // Catching: // `std::mem::transmute(std::ptr::null::())` - ExprKind::Call(func1, []) if is_path_diagnostic_item(cx, func1, sym::ptr_null) => { + ExprKind::Call(func1, []) if func1.basic_res().is_diag_item(cx, sym::ptr_null) => { lint_expr(cx, expr); true }, diff --git a/clippy_lints/src/transmute/transmuting_null.rs b/clippy_lints/src/transmute/transmuting_null.rs index 544014bd32b30..1a6262f2ff767 100644 --- a/clippy_lints/src/transmute/transmuting_null.rs +++ b/clippy_lints/src/transmute/transmuting_null.rs @@ -1,6 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; +use clippy_utils::is_integer_literal; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; @@ -35,7 +36,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t // Catching: // `std::mem::transmute(std::ptr::null::())` if let ExprKind::Call(func1, []) = arg.kind - && is_path_diagnostic_item(cx, func1, sym::ptr_null) + && func1.basic_res().is_diag_item(cx, sym::ptr_null) { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); return true; diff --git a/clippy_lints/src/types/owned_cow.rs b/clippy_lints/src/types/owned_cow.rs index 9d4d885bf6cd6..0eef373be04ea 100644 --- a/clippy_lints/src/types/owned_cow.rs +++ b/clippy_lints/src/types/owned_cow.rs @@ -34,7 +34,7 @@ fn replacement(cx: &LateContext<'_>, cty: &hir::Ty<'_>) -> Option<(Span, String) if cty.basic_res().is_lang_item(cx, hir::LangItem::String) { return Some((cty.span, "str".into())); } - if clippy_utils::is_path_diagnostic_item(cx, cty, sym::Vec) { + if cty.basic_res().is_diag_item(cx, sym::Vec) { return if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = cty.kind && let [.., last_seg] = path.segments && let Some(args) = last_seg.args @@ -46,7 +46,7 @@ fn replacement(cx: &LateContext<'_>, cty: &hir::Ty<'_>) -> Option<(Span, String) None }; } - if clippy_utils::is_path_diagnostic_item(cx, cty, sym::cstring_type) { + if cty.basic_res().is_diag_item(cx, sym::cstring_type) { return Some(( cty.span, (if clippy_utils::is_no_std_crate(cx) { @@ -59,7 +59,7 @@ fn replacement(cx: &LateContext<'_>, cty: &hir::Ty<'_>) -> Option<(Span, String) } // Neither OsString nor PathBuf are available outside std for (diag, repl) in [(sym::OsString, "std::ffi::OsStr"), (sym::PathBuf, "std::path::Path")] { - if clippy_utils::is_path_diagnostic_item(cx, cty, diag) { + if cty.basic_res().is_diag_item(cx, diag) { return Some((cty.span, repl.into())); } } diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index f25387b5e66cc..0c388fbd4812f 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -298,7 +298,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { if let Some(lang) = self.cx.tcx.lang_items().from_def_id(id) { chain!(self, "{path}.res(cx).is_lang_item(cx, LangItem::{}", lang.name()); } else if let Some(name) = self.cx.tcx.get_diagnostic_name(id) { - chain!(self, "is_path_diagnostic_item(cx, {path}, sym::{name})"); + chain!(self, "{path}.res(cx).is_diag_item(cx, sym::{name})"); } else { chain!( self, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index e92373941dc11..a976c9a7dafe1 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -438,16 +438,6 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator( - cx: &LateContext<'_>, - maybe_path: &impl MaybePath<'tcx>, - diag_item: Symbol, -) -> bool { - path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id)) -} - /// If the expression is a path to a local, returns the canonical `HirId` of the local. pub fn path_to_local(expr: &Expr<'_>) -> Option { if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind @@ -784,13 +774,13 @@ fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: & node: LitKind::Str(sym, _), .. }) => return sym.is_empty() && ty.basic_res().is_lang_item(cx, LangItem::String), - ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec), + ExprKind::Array([]) => return ty.basic_res().is_diag_item(cx, sym::Vec), ExprKind::Repeat(_, len) => { if let ConstArgKind::Anon(anon_const) = len.kind && let ExprKind::Lit(const_lit) = cx.tcx.hir_body(anon_const.body).value.kind && let LitKind::Int(v, _) = const_lit.node { - return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec); + return v == 0 && ty.basic_res().is_diag_item(cx, sym::Vec); } }, _ => (), diff --git a/tests/ui/author/blocks.stdout b/tests/ui/author/blocks.stdout index e453299edbcfc..ff9fe2425ff9f 100644 --- a/tests/ui/author/blocks.stdout +++ b/tests/ui/author/blocks.stdout @@ -23,13 +23,13 @@ if let ExprKind::Block(block, None) = expr.kind && let StmtKind::Let(local) = block.stmts[0].kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && is_path_diagnostic_item(cx, func, sym::string_new) + && func.res(cx).is_diag_item(cx, sym::string_new) && args.is_empty() && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind && name.as_str() == "expr" && let Some(trailing_expr) = block.expr && let ExprKind::Call(func1, args1) = trailing_expr.kind - && is_path_diagnostic_item(cx, func1, sym::mem_drop) + && func1.res(cx).is_diag_item(cx, sym::mem_drop) && args1.len() == 1 { // report your lint here diff --git a/tests/ui/author/call.stdout b/tests/ui/author/call.stdout index 2b179d45112ee..024121b14f9bc 100644 --- a/tests/ui/author/call.stdout +++ b/tests/ui/author/call.stdout @@ -1,7 +1,7 @@ if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && is_path_diagnostic_item(cx, func, sym::cmp_min) + && func.res(cx).is_diag_item(cx, sym::cmp_min) && args.len() == 2 && let ExprKind::Lit(ref lit) = args[0].kind && let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node diff --git a/tests/ui/author/issue_3849.stdout b/tests/ui/author/issue_3849.stdout index f02ea5bf075f7..b88058f974dba 100644 --- a/tests/ui/author/issue_3849.stdout +++ b/tests/ui/author/issue_3849.stdout @@ -1,7 +1,7 @@ if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind - && is_path_diagnostic_item(cx, func, sym::transmute) + && func.res(cx).is_diag_item(cx, sym::transmute) && args.len() == 1 && let PatKind::Wild = local.pat.kind { From 4914f5908ff18a84b5a2d53c7dbf5714f3b32c42 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 04:10:30 -0400 Subject: [PATCH 49/89] Remove `is_type_diagnostic_item` --- clippy_lints/src/arc_with_non_send_sync.rs | 5 ++- .../src/assertions_on_result_states.rs | 5 ++- clippy_lints/src/booleans.rs | 7 ++-- clippy_lints/src/cognitive_complexity.rs | 4 +- clippy_lints/src/doc/missing_headers.rs | 7 ++-- clippy_lints/src/fallible_impl_from.rs | 6 +-- clippy_lints/src/functions/result.rs | 5 ++- clippy_lints/src/if_let_mutex.rs | 4 +- clippy_lints/src/implicit_hasher.rs | 6 +-- clippy_lints/src/ineffective_open_options.rs | 8 +++- clippy_lints/src/iter_over_hash_type.rs | 6 +-- clippy_lints/src/lines_filter_map_ok.rs | 7 +++- clippy_lints/src/loops/for_kv_map.rs | 4 +- clippy_lints/src/loops/missing_spin_loop.rs | 4 +- clippy_lints/src/loops/same_item_push.rs | 5 ++- .../src/loops/unused_enumerate_index.rs | 4 +- clippy_lints/src/manual_abs_diff.rs | 5 ++- clippy_lints/src/manual_ignore_case_cmp.rs | 5 ++- clippy_lints/src/manual_let_else.rs | 4 +- clippy_lints/src/map_unit_fn.rs | 6 +-- clippy_lints/src/match_result_ok.rs | 4 +- clippy_lints/src/matches/manual_filter.rs | 4 +- clippy_lints/src/matches/manual_utils.rs | 6 +-- clippy_lints/src/matches/match_wild_enum.rs | 5 +-- .../src/matches/match_wild_err_arm.rs | 4 +- clippy_lints/src/matches/needless_match.rs | 5 ++- .../src/matches/redundant_pattern_match.rs | 5 ++- clippy_lints/src/methods/bytecount.rs | 8 +++- clippy_lints/src/methods/clear_with_drain.rs | 5 ++- clippy_lints/src/methods/err_expect.rs | 7 ++-- clippy_lints/src/methods/expect_fun_call.rs | 7 ++-- clippy_lints/src/methods/extend_with_drain.rs | 7 ++-- clippy_lints/src/methods/filetype_is_file.rs | 4 +- clippy_lints/src/methods/filter_map.rs | 4 +- clippy_lints/src/methods/flat_map_option.rs | 4 +- clippy_lints/src/methods/get_first.rs | 4 +- .../src/methods/iter_cloned_collect.rs | 5 ++- clippy_lints/src/methods/iter_count.rs | 18 ++++----- clippy_lints/src/methods/iter_kv_map.rs | 4 +- clippy_lints/src/methods/iter_next_slice.rs | 4 +- .../src/methods/join_absolute_paths.rs | 4 +- .../src/methods/manual_is_variant_and.rs | 8 ++-- clippy_lints/src/methods/manual_ok_or.rs | 8 +++- clippy_lints/src/methods/manual_str_repeat.rs | 4 +- clippy_lints/src/methods/map_clone.rs | 5 ++- .../src/methods/map_collect_result_unit.rs | 4 +- clippy_lints/src/methods/map_err_ignore.rs | 8 +++- clippy_lints/src/methods/map_flatten.rs | 4 +- clippy_lints/src/methods/map_identity.rs | 7 ++-- clippy_lints/src/methods/map_unwrap_or.rs | 6 +-- clippy_lints/src/methods/mut_mutex_lock.rs | 5 ++- .../src/methods/needless_option_as_deref.rs | 4 +- .../src/methods/needless_option_take.rs | 4 +- clippy_lints/src/methods/ok_expect.rs | 7 ++-- clippy_lints/src/methods/open_options.rs | 4 +- .../src/methods/option_as_ref_cloned.rs | 8 +++- .../src/methods/option_as_ref_deref.rs | 4 +- .../src/methods/option_map_or_none.rs | 6 +-- .../src/methods/option_map_unwrap_or.rs | 5 ++- clippy_lints/src/methods/or_fun_call.rs | 7 ++-- clippy_lints/src/methods/or_then_unwrap.rs | 6 +-- .../src/methods/path_buf_push_overwrite.rs | 8 +++- .../src/methods/path_ends_with_ext.rs | 8 +++- .../src/methods/read_line_without_trim.rs | 6 +-- .../src/methods/readonly_write_lock.rs | 9 +++-- .../src/methods/result_map_or_else_none.rs | 4 +- .../methods/suspicious_command_arg_space.rs | 4 +- .../src/methods/suspicious_to_owned.rs | 4 +- .../src/methods/unnecessary_get_then_check.rs | 6 +-- .../src/methods/unnecessary_lazy_eval.rs | 6 +-- .../methods/unnecessary_option_map_or_else.rs | 4 +- .../methods/unnecessary_result_map_or_else.rs | 4 +- .../src/methods/unnecessary_to_owned.rs | 9 ++--- .../src/methods/unused_enumerate_index.rs | 4 +- .../src/methods/unwrap_expect_used.rs | 7 ++-- .../methods/useless_nonzero_new_unchecked.rs | 4 +- clippy_lints/src/methods/utils.rs | 4 +- .../src/methods/vec_resize_to_zero.rs | 8 +++- .../src/methods/verbose_file_reads.rs | 8 +++- clippy_lints/src/missing_fields_in_debug.rs | 9 ++--- clippy_lints/src/mutex_atomic.rs | 4 +- clippy_lints/src/needless_pass_by_value.rs | 7 ++-- .../src/operators/arithmetic_side_effects.rs | 6 +-- clippy_lints/src/operators/duration_subsec.rs | 8 +++- .../src/operators/integer_division.rs | 4 +- clippy_lints/src/panic_in_result_fn.rs | 4 +- clippy_lints/src/partialeq_to_none.rs | 10 +++-- clippy_lints/src/pathbuf_init_then_push.rs | 6 +-- .../src/permissions_set_readonly_false.rs | 7 +++- clippy_lints/src/question_mark.rs | 8 ++-- clippy_lints/src/set_contains_or_insert.rs | 4 +- clippy_lints/src/single_option_map.rs | 4 +- clippy_lints/src/strlen_on_c_strings.rs | 5 ++- clippy_lints/src/swap.rs | 6 +-- clippy_lints/src/time_subtraction.rs | 15 ++++---- clippy_lints/src/to_digit_is_some.rs | 2 +- clippy_lints/src/uninit_vec.rs | 13 ++++--- clippy_lints/src/unused_peekable.rs | 9 +++-- clippy_lints/src/unused_result_ok.rs | 4 +- clippy_lints/src/useless_conversion.rs | 7 ++-- clippy_lints/src/volatile_composites.rs | 4 +- clippy_lints/src/zero_sized_map_values.rs | 5 ++- clippy_lints/src/zombie_processes.rs | 4 +- .../src/unnecessary_def_path.rs | 2 +- clippy_utils/src/higher.rs | 6 +-- clippy_utils/src/ty/mod.rs | 38 ++++++------------- 106 files changed, 349 insertions(+), 298 deletions(-) diff --git a/clippy_lints/src/arc_with_non_send_sync.rs b/clippy_lints/src/arc_with_non_send_sync.rs index 085029a744bec..acfdfa65baeda 100644 --- a/clippy_lints/src/arc_with_non_send_sync.rs +++ b/clippy_lints/src/arc_with_non_send_sync.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::implements_trait; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -46,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync { && let ExprKind::Path(QPath::TypeRelative(func_ty, func_name)) = func.kind && func_name.ident.name == sym::new && !expr.span.from_expansion() - && is_type_diagnostic_item(cx, cx.typeck_results().node_type(func_ty.hir_id), sym::Arc) + && cx.typeck_results().node_type(func_ty.hir_id).is_diag_item(cx, sym::Arc) && let arg_ty = cx.typeck_results().expr_ty(arg) // make sure that the type is not and does not contain any type parameters && arg_ty.walk().all(|arg| { diff --git a/clippy_lints/src/assertions_on_result_states.rs b/clippy_lints/src/assertions_on_result_states.rs index 08253b0c4995e..191fbf65e5a6c 100644 --- a/clippy_lints/src/assertions_on_result_states.rs +++ b/clippy_lints/src/assertions_on_result_states.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::{has_debug_impl, is_copy}; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{path_res, sym}; use rustc_errors::Applicability; @@ -55,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { && let ExprKind::MethodCall(method_segment, recv, [], _) = condition.kind && let result_type_with_refs = cx.typeck_results().expr_ty(recv) && let result_type = result_type_with_refs.peel_refs() - && is_type_diagnostic_item(cx, result_type, sym::Result) + && result_type.is_diag_item(cx, sym::Result) && let ty::Adt(_, args) = result_type.kind() { if !is_copy(cx, result_type) { diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 64aeb27df693f..f3985603c4d2e 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -2,9 +2,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::higher::has_let_expr; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::implements_trait; use clippy_utils::{eq_expr_value, sym}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -431,9 +432,7 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio }, ExprKind::MethodCall(path, receiver, args, _) => { let type_of_receiver = cx.typeck_results().expr_ty(receiver); - if !is_type_diagnostic_item(cx, type_of_receiver, sym::Option) - && !is_type_diagnostic_item(cx, type_of_receiver, sym::Result) - { + if !type_of_receiver.is_diag_item(cx, sym::Option) && !type_of_receiver.is_diag_item(cx, sym::Result) { return None; } METHODS_WITH_NEGATION diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index c0f30e456d8db..595625c08bef9 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{IntoSpan, SpanRangeExt}; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{LimitStack, get_async_fn_body, sym}; use core::ops::ControlFlow; @@ -93,7 +93,7 @@ impl CognitiveComplexity { }); let ret_ty = cx.typeck_results().node_type(expr.hir_id); - let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::Result) { + let ret_adjust = if ret_ty.is_diag_item(cx, sym::Result) { returns } else { #[expect(clippy::integer_division)] diff --git a/clippy_lints/src/doc/missing_headers.rs b/clippy_lints/src/doc/missing_headers.rs index 3033ac0d0b0b7..7c325132f8792 100644 --- a/clippy_lints/src/doc/missing_headers.rs +++ b/clippy_lints/src/doc/missing_headers.rs @@ -1,7 +1,8 @@ use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC}; use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use clippy_utils::ty::{get_type_diagnostic_name, implements_trait_with_env, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::{get_type_diagnostic_name, implements_trait_with_env}; use clippy_utils::visitors::for_each_expr; use clippy_utils::{fulfill_or_allowed, is_doc_hidden, is_inside_always_const_context, method_chain_args, return_ty}; use rustc_hir::{BodyId, FnSig, OwnerId, Safety}; @@ -62,7 +63,7 @@ pub fn check( ); } if !headers.errors { - if is_type_diagnostic_item(cx, return_ty(cx, owner_id), sym::Result) { + if return_ty(cx, owner_id).is_diag_item(cx, sym::Result) { span_lint( cx, MISSING_ERRORS_DOC, @@ -83,7 +84,7 @@ pub fn check( &[], ) && let ty::Coroutine(_, subs) = ret_ty.kind() - && is_type_diagnostic_item(cx, subs.as_coroutine().return_ty(), sym::Result) + && subs.as_coroutine().return_ty().is_diag_item(cx, sym::Result) { span_lint( cx, diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index fdfcbb540bce6..4446b912bf7ee 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::method_chain_args; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -84,9 +84,7 @@ fn lint_impl_body(cx: &LateContext<'_>, item_def_id: hir::OwnerId, impl_span: Sp // check for `unwrap` if let Some(arglists) = method_chain_args(expr, &[sym::unwrap]) { let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs(); - if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option) - || is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result) - { + if receiver_ty.is_diag_item(self.lcx, sym::Option) || receiver_ty.is_diag_item(self.lcx, sym::Result) { self.result.push(expr.span); } } diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs index 1f2fce687ed1b..fb80cc1a63a30 100644 --- a/clippy_lints/src/functions/result.rs +++ b/clippy_lints/src/functions/result.rs @@ -1,4 +1,5 @@ use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use rustc_errors::Diag; use rustc_hir as hir; use rustc_lint::{LateContext, LintContext}; @@ -6,7 +7,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, sym}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; -use clippy_utils::ty::{AdtVariantInfo, approx_ty_size, is_type_diagnostic_item}; +use clippy_utils::ty::{AdtVariantInfo, approx_ty_size}; use clippy_utils::{is_no_std_crate, trait_ref_of_method}; use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR}; @@ -24,7 +25,7 @@ fn result_err_ty<'tcx>( && let ty = cx .tcx .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(id).instantiate_identity().output()) - && is_type_diagnostic_item(cx, ty, sym::Result) + && ty.is_diag_item(cx, sym::Result) && let ty::Adt(_, args) = ty.kind() { let err_ty = args.type_at(1); diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index a99118f90f88a..eed2d5d885353 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{eq_expr_value, higher, sym}; use core::ops::ControlFlow; @@ -95,7 +95,7 @@ fn mutex_lock_call<'tcx>( if let ExprKind::MethodCall(path, self_arg, [], _) = &expr.kind && path.ident.name == sym::lock && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs() - && is_type_diagnostic_item(cx, ty, sym::Mutex) + && ty.is_diag_item(cx, sym::Mutex) && op_mutex.is_none_or(|op| eq_expr_value(cx, self_arg, op)) { ControlFlow::Break(self_arg) diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index b3c90f364e834..d2bc0b6d99354 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -1,6 +1,7 @@ use std::borrow::Cow; use std::collections::BTreeMap; +use clippy_utils::res::MaybeDef; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::{Visitor, VisitorExt, walk_body, walk_expr, walk_ty}; use rustc_hir::{self as hir, AmbigArg, Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; @@ -14,7 +15,6 @@ use rustc_span::Span; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet}; use clippy_utils::sym; -use clippy_utils::ty::is_type_diagnostic_item; declare_clippy_lint! { /// ### What it does @@ -227,14 +227,14 @@ impl<'tcx> ImplicitHasherType<'tcx> { let ty = lower_ty(cx.tcx, hir_ty); - if is_type_diagnostic_item(cx, ty, sym::HashMap) && params_len == 2 { + if ty.is_diag_item(cx, sym::HashMap) && params_len == 2 { Some(ImplicitHasherType::HashMap( hir_ty.span, ty, snippet(cx, params[0].span, "K"), snippet(cx, params[1].span, "V"), )) - } else if is_type_diagnostic_item(cx, ty, sym::HashSet) && params_len == 1 { + } else if ty.is_diag_item(cx, sym::HashSet) && params_len == 1 { Some(ImplicitHasherType::HashSet( hir_ty.span, ty, diff --git a/clippy_lints/src/ineffective_open_options.rs b/clippy_lints/src/ineffective_open_options.rs index a159f61571839..bc57d9e85478a 100644 --- a/clippy_lints/src/ineffective_open_options.rs +++ b/clippy_lints/src/ineffective_open_options.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{peel_blocks, peel_hir_expr_while, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -47,7 +47,11 @@ impl<'tcx> LateLintPass<'tcx> for IneffectiveOpenOptions { if let ExprKind::MethodCall(name, recv, [_], _) = expr.kind && name.ident.name == sym::open && !expr.span.from_expansion() - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::FsOpenOptions) + && cx + .typeck_results() + .expr_ty(recv) + .peel_refs() + .is_diag_item(cx, sym::FsOpenOptions) { let mut append = false; let mut write = None; diff --git a/clippy_lints/src/iter_over_hash_type.rs b/clippy_lints/src/iter_over_hash_type.rs index b1cb6da9475ba..6bb46ac3b5542 100644 --- a/clippy_lints/src/iter_over_hash_type.rs +++ b/clippy_lints/src/iter_over_hash_type.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::higher::ForLoop; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -55,9 +55,7 @@ impl LateLintPass<'_> for IterOverHashType { if let Some(for_loop) = ForLoop::hir(expr) && !for_loop.body.span.from_expansion() && let ty = cx.typeck_results().expr_ty(for_loop.arg).peel_refs() - && hash_iter_tys - .into_iter() - .any(|sym| is_type_diagnostic_item(cx, ty, sym)) + && hash_iter_tys.into_iter().any(|sym| ty.is_diag_item(cx, sym)) { span_lint( cx, diff --git a/clippy_lints/src/lines_filter_map_ok.rs b/clippy_lints/src/lines_filter_map_ok.rs index 14ccb6fce2239..ab00cd9ca539c 100644 --- a/clippy_lints/src/lines_filter_map_ok.rs +++ b/clippy_lints/src/lines_filter_map_ok.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::{is_diag_item_method, is_trait_method, path_to_local_id, sym}; use rustc_errors::Applicability; use rustc_hir::{Body, Closure, Expr, ExprKind}; @@ -76,7 +76,10 @@ impl LateLintPass<'_> for LinesFilterMapOk { && is_trait_method(cx, expr, sym::Iterator) && let fm_method_name = fm_method.ident.name && matches!(fm_method_name, sym::filter_map | sym::flat_map | sym::flatten) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(fm_receiver), sym::IoLines) + && cx + .typeck_results() + .expr_ty_adjusted(fm_receiver) + .is_diag_item(cx, sym::IoLines) && should_lint(cx, fm_args, fm_method_name) && self.msrv.meets(cx, msrvs::MAP_WHILE) { diff --git a/clippy_lints/src/loops/for_kv_map.rs b/clippy_lints/src/loops/for_kv_map.rs index e314bc2068b30..c6b650a1a88be 100644 --- a/clippy_lints/src/loops/for_kv_map.rs +++ b/clippy_lints/src/loops/for_kv_map.rs @@ -1,7 +1,7 @@ use super::FOR_KV_MAP; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{pat_is_wild, sugg}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx _ => arg, }; - if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap) { + if ty.is_diag_item(cx, sym::HashMap) || ty.is_diag_item(cx, sym::BTreeMap) { span_lint_and_then( cx, FOR_KV_MAP, diff --git a/clippy_lints/src/loops/missing_spin_loop.rs b/clippy_lints/src/loops/missing_spin_loop.rs index 8a2d0036203a2..d5dbcbe9dc701 100644 --- a/clippy_lints/src/loops/missing_spin_loop.rs +++ b/clippy_lints/src/loops/missing_spin_loop.rs @@ -1,7 +1,7 @@ use super::MISSING_SPIN_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::std_or_core; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind}; use rustc_lint::LateContext; @@ -40,7 +40,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &' && let ExprKind::MethodCall(method, callee, ..) = unpack_cond(cond).kind && [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name) && let callee_ty = cx.typeck_results().expr_ty(callee) - && is_type_diagnostic_item(cx, callee_ty, sym::AtomicBool) + && callee_ty.is_diag_item(cx, sym::AtomicBool) && let Some(std_or_core) = std_or_core(cx) { span_lint_and_sugg( diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index e792edbe23e0b..097b84de49255 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -1,8 +1,9 @@ use super::SAME_ITEM_PUSH; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::implements_trait; use clippy_utils::{msrvs, path_to_local, std_or_core, sym}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -186,7 +187,7 @@ fn get_vec_push<'tcx>( && let ExprKind::MethodCall(path, self_expr, [pushed_item], _) = &semi_stmt.kind // Check that the method being called is push() on a Vec && path.ident.name == sym::push - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec) + && cx.typeck_results().expr_ty(self_expr).is_diag_item(cx, sym::Vec) { return Some((self_expr, pushed_item, semi_stmt.span.ctxt())); } diff --git a/clippy_lints/src/loops/unused_enumerate_index.rs b/clippy_lints/src/loops/unused_enumerate_index.rs index 13b93d2c0097b..b893b0baad490 100644 --- a/clippy_lints/src/loops/unused_enumerate_index.rs +++ b/clippy_lints/src/loops/unused_enumerate_index.rs @@ -1,7 +1,7 @@ use super::UNUSED_ENUMERATE_INDEX; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{pat_is_wild, sugg}; use rustc_errors::Applicability; use rustc_hir::def::DefKind; @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_ && let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind && let ty = cx.typeck_results().expr_ty(arg) && pat_is_wild(cx, &index.kind, body) - && is_type_diagnostic_item(cx, ty, sym::Enumerate) + && ty.is_diag_item(cx, sym::Enumerate) && let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id) && cx.tcx.is_diagnostic_item(sym::enumerate_method, call_id) { diff --git a/clippy_lints/src/manual_abs_diff.rs b/clippy_lints/src/manual_abs_diff.rs index 5814b6815a1ea..22de5e8268bd9 100644 --- a/clippy_lints/src/manual_abs_diff.rs +++ b/clippy_lints/src/manual_abs_diff.rs @@ -2,9 +2,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::If; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::HasSession as _; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; +use clippy_utils::ty::peel_and_count_ty_refs; use clippy_utils::{eq_expr_value, peel_blocks, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -104,7 +105,7 @@ impl ManualAbsDiff { fn are_ty_eligible<'tcx>(&self, cx: &LateContext<'tcx>, a: &Expr<'_>, b: &Expr<'_>) -> Option<(Ty<'tcx>, usize)> { let is_int = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(_) | ty::Int(_)) && self.msrv.meets(cx, msrvs::ABS_DIFF); let is_duration = - |ty| is_type_diagnostic_item(cx, ty, sym::Duration) && self.msrv.meets(cx, msrvs::DURATION_ABS_DIFF); + |ty: Ty<'_>| ty.is_diag_item(cx, sym::Duration) && self.msrv.meets(cx, msrvs::DURATION_ABS_DIFF); let a_ty = cx.typeck_results().expr_ty(a).peel_refs(); let (b_ty, b_n_refs, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(b)); diff --git a/clippy_lints/src/manual_ignore_case_cmp.rs b/clippy_lints/src/manual_ignore_case_cmp.rs index f7d9ec1fae8e4..ae17b4e446e4c 100644 --- a/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/clippy_lints/src/manual_ignore_case_cmp.rs @@ -1,8 +1,9 @@ use crate::manual_ignore_case_cmp::MatchType::{Literal, ToAscii}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; -use clippy_utils::ty::{get_type_diagnostic_name, is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::ExprKind::{Binary, Lit, MethodCall}; @@ -72,7 +73,7 @@ fn get_ascii_type<'a>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'_>) -> Op fn needs_ref_to_cmp(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { ty.is_char() || *ty.kind() == ty::Uint(UintTy::U8) - || is_type_diagnostic_item(cx, ty, sym::Vec) + || ty.is_diag_item(cx, sym::Vec) || is_type_lang_item(cx, ty, LangItem::String) } diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 2705ef20b795d..3ed24b5dd2c55 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -2,8 +2,8 @@ use crate::question_mark::{QUESTION_MARK, QuestionMark}; use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{ MaybePath, is_lint_allowed, is_never_expr, is_wild, msrvs, pat_and_expr_can_be_question_mark, path_res, peel_blocks, }; @@ -384,7 +384,7 @@ fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: boo } let ty = typeck_results.pat_ty(pat); // Option and Result are allowed, everything else isn't. - if !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) { + if !(ty.is_diag_item(cx, sym::Option) || ty.is_diag_item(cx, sym::Result)) { has_disallowed = true; } }); diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 39e5289c62ae4..681dc2ab5bc0c 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context}; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{iter_input_pats, method_chain_args}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -205,9 +205,9 @@ fn lint_map_unit_fn( ) { let var_arg = &map_args.0; - let (map_type, variant, lint) = if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::Option) { + let (map_type, variant, lint) = if cx.typeck_results().expr_ty(var_arg).is_diag_item(cx, sym::Option) { ("Option", "Some", OPTION_MAP_UNIT_FN) - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::Result) { + } else if cx.typeck_results().expr_ty(var_arg).is_diag_item(cx, sym::Result) { ("Result", "Ok", RESULT_MAP_UNIT_FN) } else { return; diff --git a/clippy_lints/src/match_result_ok.rs b/clippy_lints/src/match_result_ok.rs index e0cb5d14d3c93..bc9279677b2eb 100644 --- a/clippy_lints/src/match_result_ok.rs +++ b/clippy_lints/src/match_result_ok.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{higher, is_res_lang_ctor, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, PatKind}; @@ -57,7 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind //check is expr.ok() has type Result.ok(, _) && let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind //get operation && ok_path.ident.name == sym::ok - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + && cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) && is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome) && let ctxt = expr.span.ctxt() && let_expr.span.ctxt() == ctxt diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index abf723fa6f4ca..96252a82fc0d9 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::visitors::contains_unsafe_block; use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; @@ -98,7 +98,7 @@ pub(super) fn check_match<'tcx>( expr: &'tcx Expr<'_>, ) { let ty = cx.typeck_results().expr_ty(expr); - if is_type_diagnostic_item(cx, ty, sym::Option) + if ty.is_diag_item(cx, sym::Option) && let [first_arm, second_arm] = arms && first_arm.guard.is_none() && second_arm.guard.is_none() diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index d4bfdb7e440d8..5e989beb9fd1f 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -1,8 +1,9 @@ use crate::map_unit_fn::OPTION_MAP_UNIT_FN; use crate::matches::MATCH_AS_REF; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item, is_unsafe_fn, peel_and_count_ty_refs}; +use clippy_utils::ty::{is_copy, is_unsafe_fn, peel_and_count_ty_refs}; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, @@ -33,8 +34,7 @@ where let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(scrutinee)); let ty_mutability = ty_mutability.unwrap_or(Mutability::Mut); - if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) + if !(scrutinee_ty.is_diag_item(cx, sym::Option) && cx.typeck_results().expr_ty(expr).is_diag_item(cx, sym::Option)) { return None; } diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 70a03ff937625..ac5da320bdffb 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns}; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -16,8 +16,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { let ty = cx.typeck_results().expr_ty(ex).peel_refs(); let adt_def = match ty.kind() { ty::Adt(adt_def, _) - if adt_def.is_enum() - && !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) => + if adt_def.is_enum() && !(ty.is_diag_item(cx, sym::Option) || ty.is_diag_item(cx, sym::Result)) => { adt_def }, diff --git a/clippy_lints/src/matches/match_wild_err_arm.rs b/clippy_lints/src/matches/match_wild_err_arm.rs index 8ce8453360f78..e38ba801c0bf7 100644 --- a/clippy_lints/src/matches/match_wild_err_arm.rs +++ b/clippy_lints/src/matches/match_wild_err_arm.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::macros::{is_panic, root_macro_call}; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::visitors::is_local_used; use clippy_utils::{is_in_const_context, is_wild, peel_blocks_with_stmt}; use rustc_hir::{Arm, Expr, PatKind}; @@ -16,7 +16,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<' } let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs(); - if is_type_diagnostic_item(cx, ex_ty, sym::Result) { + if ex_ty.is_diag_item(cx, sym::Result) { for arm in arms { if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind { let path_str = rustc_hir_pretty::qpath_to_string(&cx.tcx, path); diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index 3a2097c3df261..e6f47707e3a29 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -1,7 +1,8 @@ use super::NEEDLESS_MATCH; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_diagnostic_item, same_type_modulo_regions}; +use clippy_utils::ty::same_type_modulo_regions; use clippy_utils::{ SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, is_res_lang_ctor, over, path_res, peel_blocks_with_stmt, @@ -104,7 +105,7 @@ fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool return false; } let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr); - if is_type_diagnostic_item(cx, let_expr_ty, sym::Option) { + if let_expr_ty.is_diag_item(cx, sym::Option) { return is_res_lang_ctor(cx, path_res(cx, else_expr), OptionNone) || eq_expr_value(cx, if_let.let_expr, else_expr); } diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 9d0115791838c..916ed25cfd748 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -1,8 +1,9 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::walk_span_to_context; use clippy_utils::sugg::{Sugg, make_unop}; -use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; +use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures}; use clippy_utils::{higher, is_expn_of, is_trait_method, sym}; use rustc_ast::ast::LitKind; @@ -460,7 +461,7 @@ fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expecte Item::Diag(expected_ty, expected_variant) => { let ty = cx.typeck_results().pat_ty(pat); - if is_type_diagnostic_item(cx, ty, expected_ty) { + if ty.is_diag_item(cx, expected_ty) { let variant = ty .ty_adt_def() .expect("struct pattern type is not an ADT") diff --git a/clippy_lints/src/methods/bytecount.rs b/clippy_lints/src/methods/bytecount.rs index 0498f317442ac..f3a34ce6964ed 100644 --- a/clippy_lints/src/methods/bytecount.rs +++ b/clippy_lints/src/methods/bytecount.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::is_local_used; use clippy_utils::{path_to_local_id, peel_blocks, peel_ref_operators, strip_pat_refs}; use rustc_errors::Applicability; @@ -23,7 +23,11 @@ pub(super) fn check<'tcx>( && let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind && let ExprKind::Binary(ref op, l, r) = body.value.kind && op.node == BinOpKind::Eq - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv).peel_refs(), sym::SliceIter) + && cx + .typeck_results() + .expr_ty(filter_recv) + .peel_refs() + .is_diag_item(cx, sym::SliceIter) && let operand_is_arg = (|expr| { let expr = peel_ref_operators(cx, peel_blocks(expr)); path_to_local_id(expr, arg_id) diff --git a/clippy_lints/src/methods/clear_with_drain.rs b/clippy_lints/src/methods/clear_with_drain.rs index 6e24cabca8bba..77b1ec3f97883 100644 --- a/clippy_lints/src/methods/clear_with_drain.rs +++ b/clippy_lints/src/methods/clear_with_drain.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_range_full; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, QPath}; use rustc_lint::LateContext; @@ -29,7 +30,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span fn match_acceptable_type(cx: &LateContext<'_>, expr: &Expr<'_>, types: &[rustc_span::Symbol]) -> bool { let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs(); - types.iter().any(|&ty| is_type_diagnostic_item(cx, expr_ty, ty)) + types.iter().any(|&ty| expr_ty.is_diag_item(cx, ty)) // String type is a lang item but not a diagnostic item for now so we need a separate check || is_type_lang_item(cx, expr_ty, LangItem::String) } diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs index 91ddaca07d8bb..6e9aebcf18ae4 100644 --- a/clippy_lints/src/methods/err_expect.rs +++ b/clippy_lints/src/methods/err_expect.rs @@ -1,7 +1,8 @@ use super::ERR_EXPECT; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::{has_debug_impl, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::has_debug_impl; use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; @@ -16,7 +17,7 @@ pub(super) fn check( err_span: Span, msrv: Msrv, ) { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) // Grabs the `Result` type && let result_type = cx.typeck_results().expr_ty(recv) // Tests if the T type in a `Result` is not None @@ -40,7 +41,7 @@ pub(super) fn check( /// Given a `Result` type, return its data (`T`). fn get_data_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { match ty.kind() { - ty::Adt(_, args) if is_type_diagnostic_item(cx, ty, sym::Result) => args.types().next(), + ty::Adt(_, args) if ty.is_diag_item(cx, sym::Result) => args.types().next(), _ => None, } } diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index 74920a17310d5..ea49fd414457d 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span, root_macro_call_first_node}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::is_type_lang_item; use clippy_utils::visitors::for_each_expr; use clippy_utils::{contains_return, is_inside_always_const_context, peel_blocks}; use rustc_errors::Applicability; @@ -26,9 +27,9 @@ pub(super) fn check<'tcx>( let arg_root = get_arg_root(cx, arg); if contains_call(cx, arg_root) && !contains_return(arg_root) { let receiver_type = cx.typeck_results().expr_ty_adjusted(receiver); - let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym::Option) { + let closure_args = if receiver_type.is_diag_item(cx, sym::Option) { "||" - } else if is_type_diagnostic_item(cx, receiver_type, sym::Result) { + } else if receiver_type.is_diag_item(cx, sym::Result) { "|_|" } else { return; diff --git a/clippy_lints/src/methods/extend_with_drain.rs b/clippy_lints/src/methods/extend_with_drain.rs index db60061904f6f..381e86173f85c 100644 --- a/clippy_lints/src/methods/extend_with_drain.rs +++ b/clippy_lints/src/methods/extend_with_drain.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; @@ -10,7 +11,7 @@ use super::EXTEND_WITH_DRAIN; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if is_type_diagnostic_item(cx, ty, sym::Vec) + if ty.is_diag_item(cx, sym::Vec) //check source object && let ExprKind::MethodCall(src_method, drain_vec, [drain_arg], _) = &arg.kind && src_method.ident.name == sym::drain @@ -18,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: //check if actual src type is mutable for code suggestion && let immutable = src_ty.is_mutable_ptr() && let src_ty = src_ty.peel_refs() - && is_type_diagnostic_item(cx, src_ty, sym::Vec) + && src_ty.is_diag_item(cx, sym::Vec) //check drain range && let src_ty_range = cx.typeck_results().expr_ty(drain_arg).peel_refs() && is_type_lang_item(cx, src_ty_range, LangItem::RangeFull) diff --git a/clippy_lints/src/methods/filetype_is_file.rs b/clippy_lints/src/methods/filetype_is_file.rs index 35008c39c084d..a964cbf657b4d 100644 --- a/clippy_lints/src/methods/filetype_is_file.rs +++ b/clippy_lints/src/methods/filetype_is_file.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_parent_expr; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::{Span, sym}; @@ -10,7 +10,7 @@ use super::FILETYPE_IS_FILE; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { let ty = cx.typeck_results().expr_ty(recv); - if !is_type_diagnostic_item(cx, ty, sym::FileType) { + if !ty.is_diag_item(cx, sym::FileType) { return; } diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index dc742fa058cb6..d813105165cef 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::{is_panic, matching_root_macro_call, root_macro_call}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{indent_of, reindent_multiline, snippet}; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{SpanlessEq, higher, is_trait_method, path_to_local_id, peel_blocks, sym}; use hir::{Body, HirId, MatchSource, Pat}; use rustc_errors::Applicability; @@ -267,7 +267,7 @@ fn is_filter_some_map_unwrap( map_arg: &Expr<'_>, ) -> bool { let iterator = is_trait_method(cx, expr, sym::Iterator); - let option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv), sym::Option); + let option = cx.typeck_results().expr_ty(filter_recv).is_diag_item(cx, sym::Option); (iterator || option) && is_option_filter_map(cx, filter_arg, map_arg) } diff --git a/clippy_lints/src/methods/flat_map_option.rs b/clippy_lints/src/methods/flat_map_option.rs index 3242dcadb7013..3a7892715ed7d 100644 --- a/clippy_lints/src/methods/flat_map_option.rs +++ b/clippy_lints/src/methods/flat_map_option.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_trait_method; +use clippy_utils::res::MaybeDef; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -7,7 +8,6 @@ use rustc_middle::ty; use rustc_span::{Span, sym}; use super::FLAT_MAP_OPTION; -use clippy_utils::ty::is_type_diagnostic_item; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) { if !is_trait_method(cx, expr, sym::Iterator) { @@ -19,7 +19,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg _ if arg_ty.is_fn() => arg_ty.fn_sig(cx.tcx), _ => return, }; - if !is_type_diagnostic_item(cx, sig.output().skip_binder(), sym::Option) { + if !sig.output().skip_binder().is_diag_item(cx, sym::Option) { return; } span_lint_and_sugg( diff --git a/clippy_lints/src/methods/get_first.rs b/clippy_lints/src/methods/get_first.rs index 2e1d71ce284d6..88e9f2fdaee3e 100644 --- a/clippy_lints/src/methods/get_first.rs +++ b/clippy_lints/src/methods/get_first.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -37,7 +37,7 @@ pub(super) fn check<'tcx>( format!("{slice_name}.first()"), app, ); - } else if is_type_diagnostic_item(cx, identity, sym::VecDeque) { + } else if identity.is_diag_item(cx, sym::VecDeque) { let mut app = Applicability::MachineApplicable; let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app); span_lint_and_sugg( diff --git a/clippy_lints/src/methods/iter_cloned_collect.rs b/clippy_lints/src/methods/iter_cloned_collect.rs index b4ab313fe98d1..b1a0b658a84ce 100644 --- a/clippy_lints/src/methods/iter_cloned_collect.rs +++ b/clippy_lints/src/methods/iter_cloned_collect.rs @@ -1,6 +1,7 @@ use crate::methods::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::{get_iterator_item_ty, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::get_iterator_item_ty; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -16,7 +17,7 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'_>, ) { let expr_ty = cx.typeck_results().expr_ty(expr); - if is_type_diagnostic_item(cx, expr_ty, sym::Vec) + if expr_ty.is_diag_item(cx, sym::Vec) && let Some(slice) = derefs_to_slice(cx, recv, cx.typeck_results().expr_ty(recv)) && let ty::Adt(_, args) = expr_ty.kind() && let Some(iter_item_ty) = get_iterator_item_ty(cx, cx.typeck_results().expr_ty(recv)) diff --git a/clippy_lints/src/methods/iter_count.rs b/clippy_lints/src/methods/iter_count.rs index 6b64cc8b50ae7..ea2508cd7f382 100644 --- a/clippy_lints/src/methods/iter_count.rs +++ b/clippy_lints/src/methods/iter_count.rs @@ -1,7 +1,7 @@ use super::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -13,21 +13,21 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E let ty = cx.typeck_results().expr_ty(recv); let caller_type = if derefs_to_slice(cx, recv, ty).is_some() { "slice" - } else if is_type_diagnostic_item(cx, ty, sym::Vec) { + } else if ty.is_diag_item(cx, sym::Vec) { "Vec" - } else if is_type_diagnostic_item(cx, ty, sym::VecDeque) { + } else if ty.is_diag_item(cx, sym::VecDeque) { "VecDeque" - } else if is_type_diagnostic_item(cx, ty, sym::HashSet) { + } else if ty.is_diag_item(cx, sym::HashSet) { "HashSet" - } else if is_type_diagnostic_item(cx, ty, sym::HashMap) { + } else if ty.is_diag_item(cx, sym::HashMap) { "HashMap" - } else if is_type_diagnostic_item(cx, ty, sym::BTreeMap) { + } else if ty.is_diag_item(cx, sym::BTreeMap) { "BTreeMap" - } else if is_type_diagnostic_item(cx, ty, sym::BTreeSet) { + } else if ty.is_diag_item(cx, sym::BTreeSet) { "BTreeSet" - } else if is_type_diagnostic_item(cx, ty, sym::LinkedList) { + } else if ty.is_diag_item(cx, sym::LinkedList) { "LinkedList" - } else if is_type_diagnostic_item(cx, ty, sym::BinaryHeap) { + } else if ty.is_diag_item(cx, sym::BinaryHeap) { "BinaryHeap" } else { return; diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs index cbb1b450e60fc..2d6bc36dc5359 100644 --- a/clippy_lints/src/methods/iter_kv_map.rs +++ b/clippy_lints/src/methods/iter_kv_map.rs @@ -1,8 +1,8 @@ use super::ITER_KV_MAP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{pat_is_wild, sym}; use rustc_hir::{Body, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; @@ -38,7 +38,7 @@ pub(super) fn check<'tcx>( _ => return, } && let ty = cx.typeck_results().expr_ty_adjusted(recv).peel_refs() - && (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap)) + && (ty.is_diag_item(cx, sym::HashMap) || ty.is_diag_item(cx, sym::BTreeMap)) { let mut applicability = rustc_errors::Applicability::MachineApplicable; let recv_snippet = snippet_with_applicability(cx, recv.span, "map", &mut applicability); diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index fd4650e1e45f6..01f35ff02d44a 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -1,7 +1,7 @@ use super::utils::derefs_to_slice; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, higher}; use rustc_ast::ast; use rustc_errors::Applicability; @@ -75,6 +75,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal } fn is_vec_or_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) -> bool { - is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) + cx.typeck_results().expr_ty(expr).is_diag_item(cx, sym::Vec) || matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _)) } diff --git a/clippy_lints/src/methods/join_absolute_paths.rs b/clippy_lints/src/methods/join_absolute_paths.rs index 2ad070793cbb2..e84b7452c7584 100644 --- a/clippy_lints/src/methods/join_absolute_paths.rs +++ b/clippy_lints/src/methods/join_absolute_paths.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::expr_or_init; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -13,7 +13,7 @@ use super::JOIN_ABSOLUTE_PATHS; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx Expr<'tcx>, join_arg: &'tcx Expr<'tcx>, expr_span: Span) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if (is_type_diagnostic_item(cx, ty, sym::Path) || is_type_diagnostic_item(cx, ty, sym::PathBuf)) + if (ty.is_diag_item(cx, sym::Path) || ty.is_diag_item(cx, sym::PathBuf)) && let ExprKind::Lit(spanned) = expr_or_init(cx, join_arg).kind && let LitKind::Str(symbol, _) = spanned.node && let sym_str = symbol.as_str() diff --git a/clippy_lints/src/methods/manual_is_variant_and.rs b/clippy_lints/src/methods/manual_is_variant_and.rs index 93325ca488e4e..8f65858987b9c 100644 --- a/clippy_lints/src/methods/manual_is_variant_and.rs +++ b/clippy_lints/src/methods/manual_is_variant_and.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -30,8 +30,8 @@ pub(super) fn check( } // 2. the caller of `map()` is neither `Option` nor `Result` - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(map_recv), sym::Option); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(map_recv), sym::Result); + let is_option = cx.typeck_results().expr_ty(map_recv).is_diag_item(cx, sym::Option); + let is_result = cx.typeck_results().expr_ty(map_recv).is_diag_item(cx, sym::Result); if !is_option && !is_result { return; } @@ -208,7 +208,7 @@ pub(super) fn check_map(cx: &LateContext<'_>, expr: &Expr<'_>) { && cx.tcx.is_diagnostic_item(flavor.symbol(), adt.did()) && args.type_at(0).is_bool() && let ExprKind::MethodCall(_, recv, [map_expr], _) = expr2.kind - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), flavor.symbol()) + && cx.typeck_results().expr_ty(recv).is_diag_item(cx, flavor.symbol()) && let Ok(map_func) = MapFunc::try_from(map_expr) { return emit_lint(cx, parent_expr.span, op, flavor, bool_cst, map_func, recv); diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs index 077957fa44dc8..610368aabc5b5 100644 --- a/clippy_lints/src/methods/manual_ok_or.rs +++ b/clippy_lints/src/methods/manual_ok_or.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; use rustc_errors::Applicability; use rustc_hir::LangItem::{ResultErr, ResultOk}; @@ -19,7 +19,11 @@ pub(super) fn check<'tcx>( ) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Option) + && cx + .tcx + .type_of(impl_id) + .instantiate_identity() + .is_diag_item(cx, sym::Option) && let ExprKind::Call(err_path, [err_arg]) = or_expr.kind && is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr) && is_ok_wrapping(cx, map_expr) diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index 7d6f56e4b31b6..a7052040c7c05 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::is_type_lang_item; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; @@ -37,7 +37,7 @@ fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { let ty = cx.typeck_results().expr_ty(e); if is_type_lang_item(cx, ty, LangItem::String) || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).is_some_and(Ty::is_str)) - || (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).is_some_and(Ty::is_str)) + || (ty.is_diag_item(cx, sym::Cow) && get_ty_param(ty).is_some_and(Ty::is_str)) { Some(RepeatKind::String) } else { diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 748be9bfcc626..5568f77927ab0 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item, should_call_clone_as_function}; +use clippy_utils::ty::{is_copy, should_call_clone_as_function}; use clippy_utils::{is_diag_trait_item, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -25,7 +26,7 @@ fn should_run_lint(cx: &LateContext<'_>, e: &hir::Expr<'_>, method_id: DefId) -> // We check if it's an `Option` or a `Result`. if let Some(id) = cx.tcx.impl_of_assoc(method_id) { let identity = cx.tcx.type_of(id).instantiate_identity(); - if !is_type_diagnostic_item(cx, identity, sym::Option) && !is_type_diagnostic_item(cx, identity, sym::Result) { + if !identity.is_diag_item(cx, sym::Option) && !identity.is_diag_item(cx, sym::Result) { return false; } } else { diff --git a/clippy_lints/src/methods/map_collect_result_unit.rs b/clippy_lints/src/methods/map_collect_result_unit.rs index e944eac91e7ae..1112fbc2a1c74 100644 --- a/clippy_lints/src/methods/map_collect_result_unit.rs +++ b/clippy_lints/src/methods/map_collect_result_unit.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -12,7 +12,7 @@ use super::MAP_COLLECT_RESULT_UNIT; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, iter: &hir::Expr<'_>, map_fn: &hir::Expr<'_>) { // return of collect `Result<(),_>` let collect_ret_ty = cx.typeck_results().expr_ty(expr); - if is_type_diagnostic_item(cx, collect_ret_ty, sym::Result) + if collect_ret_ty.is_diag_item(cx, sym::Result) && let ty::Adt(_, args) = collect_ret_ty.kind() && let Some(result_t) = args.types().next() && result_t.is_unit() diff --git a/clippy_lints/src/methods/map_err_ignore.rs b/clippy_lints/src/methods/map_err_ignore.rs index 41beda9c5cb4a..f7da24bed2b80 100644 --- a/clippy_lints/src/methods/map_err_ignore.rs +++ b/clippy_lints/src/methods/map_err_ignore.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; use rustc_span::sym; @@ -9,7 +9,11 @@ use super::MAP_ERR_IGNORE; pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, arg: &Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Result) + && cx + .tcx + .type_of(impl_id) + .instantiate_identity() + .is_diag_item(cx, sym::Result) && let ExprKind::Closure(&Closure { capture_clause: CaptureBy::Ref, body, diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index 750f933330a25..18827d71110ec 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_trait_method, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -69,7 +69,7 @@ fn is_map_to_option(cx: &LateContext<'_>, map_arg: &Expr<'_>) -> bool { _ => map_closure_ty.fn_sig(cx.tcx), }; let map_closure_return_ty = cx.tcx.instantiate_bound_regions_with_erased(map_closure_sig.output()); - is_type_diagnostic_item(cx, map_closure_return_ty, sym::Option) + map_closure_return_ty.is_diag_item(cx, sym::Option) }, _ => false, } diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs index 6190c43578e9b..f68967c750ef7 100644 --- a/clippy_lints/src/methods/map_identity.rs +++ b/clippy_lints/src/methods/map_identity.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::is_copy; use clippy_utils::{is_expr_untyped_identity_function, is_mutable, is_trait_method, path_to_local_with_projections}; use rustc_errors::Applicability; use rustc_hir::{self as hir, ExprKind, Node, PatKind}; @@ -22,8 +23,8 @@ pub(super) fn check( let caller_ty = cx.typeck_results().expr_ty(caller); if (is_trait_method(cx, expr, sym::Iterator) - || is_type_diagnostic_item(cx, caller_ty, sym::Result) - || is_type_diagnostic_item(cx, caller_ty, sym::Option)) + || caller_ty.is_diag_item(cx, sym::Result) + || caller_ty.is_diag_item(cx, sym::Option)) && is_expr_untyped_identity_function(cx, map_arg) && let Some(call_span) = expr.span.trim_start(caller.span) { diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index df5a0de3392b0..62bdc4a3e4111 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::mutated_variables; use rustc_errors::Applicability; use rustc_hir as hir; @@ -22,8 +22,8 @@ pub(super) fn check<'tcx>( msrv: Msrv, ) -> bool { // lint if the caller of `map()` is an `Option` or a `Result`. - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); + let is_option = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option); + let is_result = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result); if is_result && !msrv.meets(cx, msrvs::RESULT_MAP_OR_ELSE) { return false; diff --git a/clippy_lints/src/methods/mut_mutex_lock.rs b/clippy_lints/src/methods/mut_mutex_lock.rs index 9d2c5e6232d65..c9264e747b56d 100644 --- a/clippy_lints/src/methods/mut_mutex_lock.rs +++ b/clippy_lints/src/methods/mut_mutex_lock.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::expr_custom_deref_adjustment; -use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::peel_and_count_ty_refs; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; @@ -13,7 +14,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &' && let (_, _, Some(Mutability::Mut)) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(recv)) && let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Mutex) + && cx.tcx.type_of(impl_id).is_diag_item(cx, sym::Mutex) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/needless_option_as_deref.rs b/clippy_lints/src/methods/needless_option_as_deref.rs index d77d044340dca..b4361448404a1 100644 --- a/clippy_lints/src/methods/needless_option_as_deref.rs +++ b/clippy_lints/src/methods/needless_option_as_deref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{path_res, sym}; use rustc_errors::Applicability; @@ -15,7 +15,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name let typeck = cx.typeck_results(); let outer_ty = typeck.expr_ty(expr); - if is_type_diagnostic_item(cx, outer_ty, sym::Option) && outer_ty == typeck.expr_ty(recv) { + if outer_ty.is_diag_item(cx, sym::Option) && outer_ty == typeck.expr_ty(recv) { if name == sym::as_deref_mut && recv.is_syntactic_place_expr() { let Res::Local(binding_id) = path_res(cx, recv) else { return; diff --git a/clippy_lints/src/methods/needless_option_take.rs b/clippy_lints/src/methods/needless_option_take.rs index 1544a12e6ba83..1622fdb88bd53 100644 --- a/clippy_lints/src/methods/needless_option_take.rs +++ b/clippy_lints/src/methods/needless_option_take.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let expr_type = cx.typeck_results().expr_ty(expr); - is_type_diagnostic_item(cx, expr_type, sym::Option) + expr_type.is_diag_item(cx, sym::Option) } /// Returns the string of the function call that creates the temporary. diff --git a/clippy_lints/src/methods/ok_expect.rs b/clippy_lints/src/methods/ok_expect.rs index e10bc0216e5fd..c9c1f4865b813 100644 --- a/clippy_lints/src/methods/ok_expect.rs +++ b/clippy_lints/src/methods/ok_expect.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{has_debug_impl, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::has_debug_impl; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; @@ -9,7 +10,7 @@ use super::OK_EXPECT; /// lint use of `ok().expect()` for `Result`s pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) // lint if the caller of `ok()` is a `Result` && let result_type = cx.typeck_results().expr_ty(recv) && let Some(error_type) = get_error_type(cx, result_type) @@ -29,7 +30,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr /// Given a `Result` type, return its error type (`E`). fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { match ty.kind() { - ty::Adt(_, args) if is_type_diagnostic_item(cx, ty, sym::Result) => args.types().nth(1), + ty::Adt(_, args) if ty.is_diag_item(cx, sym::Result) => args.types().nth(1), _ => None, } } diff --git a/clippy_lints/src/methods/open_options.rs b/clippy_lints/src/methods/open_options.rs index 37a8e25bef96a..1b520a9edb566 100644 --- a/clippy_lints/src/methods/open_options.rs +++ b/clippy_lints/src/methods/open_options.rs @@ -1,7 +1,7 @@ +use clippy_utils::res::MaybeDef; use rustc_data_structures::fx::FxHashMap; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{paths, sym}; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; @@ -13,7 +13,7 @@ use rustc_span::source_map::Spanned; use super::{NONSENSICAL_OPEN_OPTIONS, SUSPICIOUS_OPEN_OPTIONS}; fn is_open_options(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_diagnostic_item(cx, ty, sym::FsOpenOptions) || paths::TOKIO_IO_OPEN_OPTIONS.matches_ty(cx, ty) + ty.is_diag_item(cx, sym::FsOpenOptions) || paths::TOKIO_IO_OPEN_OPTIONS.matches_ty(cx, ty) } pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/methods/option_as_ref_cloned.rs b/clippy_lints/src/methods/option_as_ref_cloned.rs index 3c38deca6cd1f..591f6aacaef87 100644 --- a/clippy_lints/src/methods/option_as_ref_cloned.rs +++ b/clippy_lints/src/methods/option_as_ref_cloned.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::sym; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -11,7 +11,11 @@ use super::{OPTION_AS_REF_CLONED, method_call}; pub(super) fn check(cx: &LateContext<'_>, cloned_recv: &Expr<'_>, cloned_ident_span: Span) { if let Some((method @ (sym::as_ref | sym::as_mut), as_ref_recv, [], as_ref_ident_span, _)) = method_call(cloned_recv) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(as_ref_recv).peel_refs(), sym::Option) + && cx + .typeck_results() + .expr_ty(as_ref_recv) + .peel_refs() + .is_diag_item(cx, sym::Option) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 906ead16fd0d1..4c99c34892f45 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{path_to_local_id, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -23,7 +23,7 @@ pub(super) fn check( let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not); let option_ty = cx.typeck_results().expr_ty(as_ref_recv); - if !is_type_diagnostic_item(cx, option_ty, sym::Option) { + if !option_ty.is_diag_item(cx, sym::Option) { return; } diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 1a273f77fb7d0..2b94ecd470646 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_res_lang_ctor, path_def_id, path_res}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -37,8 +37,8 @@ pub(super) fn check<'tcx>( def_arg: &'tcx hir::Expr<'_>, map_arg: &'tcx hir::Expr<'_>, ) { - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); + let is_option = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option); + let is_result = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result); // There are two variants of this `map_or` lint: // (1) using `map_or` as an adapter from `Result` to `Option` diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index 4ba8e01090427..32a9b4fe7c58b 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::is_copy; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -27,7 +28,7 @@ pub(super) fn check<'tcx>( msrv: Msrv, ) { // lint if the caller of `map()` is an `Option` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option) { + if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option) { if !is_copy(cx, cx.typeck_results().expr_ty(unwrap_arg)) { // Replacing `.map().unwrap_or()` with `.map_or(, )` can sometimes lead to // borrowck errors, see #10579 for one such instance. diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 04e4503e4097f..aed4a0075c2fd 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -4,8 +4,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; use clippy_utils::higher::VecArgs; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::{expr_type_is_certain, implements_trait}; use clippy_utils::visitors::for_each_expr; use clippy_utils::{ contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment, peel_blocks, sym, @@ -113,7 +114,7 @@ fn check_unwrap_or_default( let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); // Check MSRV, but only for `Result::unwrap_or_default` - if is_type_diagnostic_item(cx, receiver_ty, sym::Result) && !msrv.meets(cx, msrvs::RESULT_UNWRAP_OR_DEFAULT) { + if receiver_ty.is_diag_item(cx, sym::Result) && !msrv.meets(cx, msrvs::RESULT_UNWRAP_OR_DEFAULT) { return false; } @@ -239,7 +240,7 @@ fn check_or_fn_call<'tcx>( && let self_ty = cx.typeck_results().expr_ty(self_expr) && let Some(&(_, fn_has_arguments, _, suffix)) = KNOW_TYPES .iter() - .find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0) && i.2.contains(&name)) + .find(|&&i| self_ty.is_diag_item(cx, i.0) && i.2.contains(&name)) { let ctxt = span.ctxt(); let mut app = Applicability::HasPlaceholders; diff --git a/clippy_lints/src/methods/or_then_unwrap.rs b/clippy_lints/src/methods/or_then_unwrap.rs index 1a760ea733d7f..f99e68fc419de 100644 --- a/clippy_lints/src/methods/or_then_unwrap.rs +++ b/clippy_lints/src/methods/or_then_unwrap.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_res_lang_ctor, path_res}; use rustc_errors::Applicability; use rustc_hir::lang_items::LangItem; @@ -21,14 +21,14 @@ pub(super) fn check<'tcx>( let title; let or_arg_content: Span; - if is_type_diagnostic_item(cx, ty, sym::Option) { + if ty.is_diag_item(cx, sym::Option) { title = "found `.or(Some(…)).unwrap()`"; if let Some(content) = get_content_if_ctor_matches(cx, or_arg, LangItem::OptionSome) { or_arg_content = content; } else { return; } - } else if is_type_diagnostic_item(cx, ty, sym::Result) { + } else if ty.is_diag_item(cx, sym::Result) { title = "found `.or(Ok(…)).unwrap()`"; if let Some(content) = get_content_if_ctor_matches(cx, or_arg, LangItem::ResultOk) { or_arg_content = content; diff --git a/clippy_lints/src/methods/path_buf_push_overwrite.rs b/clippy_lints/src/methods/path_buf_push_overwrite.rs index 32752ef7435fb..18046ff24f9d2 100644 --- a/clippy_lints/src/methods/path_buf_push_overwrite.rs +++ b/clippy_lints/src/methods/path_buf_push_overwrite.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -12,7 +12,11 @@ use super::PATH_BUF_PUSH_OVERWRITE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::PathBuf) + && cx + .tcx + .type_of(impl_id) + .instantiate_identity() + .is_diag_item(cx, sym::PathBuf) && let ExprKind::Lit(lit) = arg.kind && let LitKind::Str(ref path_lit, _) = lit.node && let pushed_path = Path::new(path_lit.as_str()) diff --git a/clippy_lints/src/methods/path_ends_with_ext.rs b/clippy_lints/src/methods/path_ends_with_ext.rs index d3f513e7abd27..87d1aa62b2c22 100644 --- a/clippy_lints/src/methods/path_ends_with_ext.rs +++ b/clippy_lints/src/methods/path_ends_with_ext.rs @@ -1,8 +1,8 @@ use super::PATH_ENDS_WITH_EXT; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::{LitKind, StrStyle}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -23,7 +23,11 @@ pub(super) fn check( msrv: Msrv, allowed_dotfiles: &FxHashSet<&'static str>, ) { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::Path) + if cx + .typeck_results() + .expr_ty(recv) + .peel_refs() + .is_diag_item(cx, sym::Path) && !path.span.from_expansion() && let ExprKind::Lit(lit) = path.kind && let LitKind::Str(path, StrStyle::Cooked) = lit.node diff --git a/clippy_lints/src/methods/read_line_without_trim.rs b/clippy_lints/src/methods/read_line_without_trim.rs index 6738bbf0a12b9..a6dfbada53483 100644 --- a/clippy_lints/src/methods/read_line_without_trim.rs +++ b/clippy_lints/src/methods/read_line_without_trim.rs @@ -1,8 +1,8 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_local_use_after_expr; use clippy_utils::{get_parent_expr, sym}; use rustc_ast::LitKind; @@ -32,7 +32,7 @@ fn parse_fails_on_trailing_newline(ty: Ty<'_>) -> bool { pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { let recv_ty = cx.typeck_results().expr_ty(recv); - if is_type_diagnostic_item(cx, recv_ty, sym::Stdin) + if recv_ty.is_diag_item(cx, sym::Stdin) && let ExprKind::Path(QPath::Resolved(_, path)) = arg.peel_borrows().kind && let Res::Local(local_id) = path.res { @@ -45,7 +45,7 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr< if args.is_empty() && segment.ident.name == sym::parse && let parse_result_ty = cx.typeck_results().expr_ty(parent) - && is_type_diagnostic_item(cx, parse_result_ty, sym::Result) + && parse_result_ty.is_diag_item(cx, sym::Result) && let ty::Adt(_, substs) = parse_result_ty.kind() && let Some(ok_ty) = substs[0].as_type() && parse_fails_on_trailing_newline(ok_ty) diff --git a/clippy_lints/src/methods/readonly_write_lock.rs b/clippy_lints/src/methods/readonly_write_lock.rs index 40b6becd45321..a98a807d1a3ce 100644 --- a/clippy_lints/src/methods/readonly_write_lock.rs +++ b/clippy_lints/src/methods/readonly_write_lock.rs @@ -1,8 +1,8 @@ use super::READONLY_WRITE_LOCK; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::mir::{enclosing_mir, visit_local_usage}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, PatKind}; use rustc_lint::LateContext; @@ -13,14 +13,17 @@ fn is_unwrap_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let ExprKind::MethodCall(path, receiver, [], _) = expr.kind && path.ident.name == sym::unwrap { - is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver).peel_refs(), sym::Result) + cx.typeck_results() + .expr_ty(receiver) + .peel_refs() + .is_diag_item(cx, sym::Result) } else { false } } pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, receiver: &Expr<'_>) { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver).peel_refs(), sym::RwLock) + if cx.typeck_results().expr_ty(receiver).peel_refs().is_diag_item(cx, sym::RwLock) && let Node::Expr(unwrap_call_expr) = cx.tcx.parent_hir_node(expr.hir_id) && is_unwrap_call(cx, unwrap_call_expr) && let parent = cx.tcx.parent_hir_node(unwrap_call_expr.hir_id) diff --git a/clippy_lints/src/methods/result_map_or_else_none.rs b/clippy_lints/src/methods/result_map_or_else_none.rs index af619c9e3bb17..7aeda9493c2c0 100644 --- a/clippy_lints/src/methods/result_map_or_else_none.rs +++ b/clippy_lints/src/methods/result_map_or_else_none.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -19,7 +19,7 @@ pub(super) fn check<'tcx>( map_arg: &'tcx hir::Expr<'_>, ) { // lint if the caller of `map_or_else()` is a `Result` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) // We check that it is mapped as `Some`. && is_res_lang_ctor(cx, path_res(cx, map_arg), OptionSome) && let hir::ExprKind::Closure(&hir::Closure { body, .. }) = def_arg.kind diff --git a/clippy_lints/src/methods/suspicious_command_arg_space.rs b/clippy_lints/src/methods/suspicious_command_arg_space.rs index c60a49067ec09..3e9c677fe34a2 100644 --- a/clippy_lints/src/methods/suspicious_command_arg_space.rs +++ b/clippy_lints/src/methods/suspicious_command_arg_space.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_errors::{Applicability, Diag}; use rustc_lint::LateContext; use rustc_span::{Span, sym}; @@ -10,7 +10,7 @@ use super::SUSPICIOUS_COMMAND_ARG_SPACE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if is_type_diagnostic_item(cx, ty, sym::Command) + if ty.is_diag_item(cx, sym::Command) && let hir::ExprKind::Lit(lit) = &arg.kind && let ast::LitKind::Str(s, _) = &lit.node && let Some((arg1, arg2)) = s.as_str().split_once(' ') diff --git a/clippy_lints/src/methods/suspicious_to_owned.rs b/clippy_lints/src/methods/suspicious_to_owned.rs index ffc237e3c24cc..9a9c09bc3d062 100644 --- a/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/clippy_lints/src/methods/suspicious_to_owned.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_diag_trait_item; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -14,7 +14,7 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && is_diag_trait_item(cx, method_def_id, sym::ToOwned) && let input_type = cx.typeck_results().expr_ty(expr) - && is_type_diagnostic_item(cx, input_type, sym::Cow) + && input_type.is_diag_item(cx, sym::Cow) { let mut app = Applicability::MaybeIncorrect; let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; diff --git a/clippy_lints/src/methods/unnecessary_get_then_check.rs b/clippy_lints/src/methods/unnecessary_get_then_check.rs index 39fce2c40c91d..10ea0c0c3e23e 100644 --- a/clippy_lints/src/methods/unnecessary_get_then_check.rs +++ b/clippy_lints/src/methods/unnecessary_get_then_check.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -11,11 +11,11 @@ use rustc_span::{Span, sym}; use super::UNNECESSARY_GET_THEN_CHECK; fn is_a_std_set_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_diagnostic_item(cx, ty, sym::HashSet) || is_type_diagnostic_item(cx, ty, sym::BTreeSet) + ty.is_diag_item(cx, sym::HashSet) || ty.is_diag_item(cx, sym::BTreeSet) } fn is_a_std_map_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap) + ty.is_diag_item(cx, sym::HashMap) || ty.is_diag_item(cx, sym::BTreeMap) } pub(super) fn check( diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 71e606add526e..2869547650f31 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{eager_or_lazy, is_from_proc_macro, usage}; use hir::FnRetTy; use rustc_errors::Applicability; @@ -19,8 +19,8 @@ pub(super) fn check<'tcx>( arg: &'tcx hir::Expr<'_>, simplify_using: &str, ) -> bool { - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); + let is_option = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option); + let is_result = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result); let is_bool = cx.typeck_results().expr_ty(recv).is_bool(); if (is_option || is_result || is_bool) diff --git a/clippy_lints/src/methods/unnecessary_option_map_or_else.rs b/clippy_lints/src/methods/unnecessary_option_map_or_else.rs index 35b72fe8fc194..265619e263765 100644 --- a/clippy_lints/src/methods/unnecessary_option_map_or_else.rs +++ b/clippy_lints/src/methods/unnecessary_option_map_or_else.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{expr_or_init, find_binding_init, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -75,7 +75,7 @@ fn handle_fn_body(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_ar /// lint use of `_.map_or_else(|err| err, |n| n)` for `Option`s. pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, map_arg: &Expr<'_>) { // lint if the caller of `map_or_else()` is an `Option` - if !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option) { + if !cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option) { return; } match map_arg.kind { 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 f84d0d6dff0af..1f6bb60414ae1 100644 --- a/clippy_lints/src/methods/unnecessary_result_map_or_else.rs +++ b/clippy_lints/src/methods/unnecessary_result_map_or_else.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::peel_blocks; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath}; @@ -51,7 +51,7 @@ pub(super) fn check<'tcx>( map_arg: &'tcx Expr<'_>, ) { // lint if the caller of `map_or_else()` is a `Result` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) && let ExprKind::Closure(&Closure { body, .. }) = map_arg.kind && let body = cx.tcx.hir_body(body) && let Some(first_param) = body.params.first() diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 6d927aef8b024..e57cfae88f1f3 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -2,10 +2,9 @@ use super::implicit_clone::is_clone_like; use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{ - get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item, peel_and_count_ty_refs, -}; +use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_lang_item, peel_and_count_ty_refs}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, return_ty, sym, @@ -220,7 +219,7 @@ fn check_into_iter_call_arg( && let Some(item_ty) = get_iterator_item_ty(cx, parent_ty) && let Some(receiver_snippet) = receiver.span.get_source_text(cx) // If the receiver is a `Cow`, we can't remove the `into_owned` generally, see https://github.com/rust-lang/rust-clippy/issues/13624. - && !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Cow) + && !cx.typeck_results().expr_ty(receiver).is_diag_item(cx, sym::Cow) // Calling `iter()` on a temporary object can lead to false positives. #14242 && !is_expr_temporary_value(cx, receiver) { @@ -678,7 +677,7 @@ fn is_str_and_string(cx: &LateContext<'_>, arg_ty: Ty<'_>, original_arg_ty: Ty<' fn is_slice_and_vec(cx: &LateContext<'_>, arg_ty: Ty<'_>, original_arg_ty: Ty<'_>) -> bool { (original_arg_ty.is_slice() || original_arg_ty.is_array() || original_arg_ty.is_array_slice()) - && is_type_diagnostic_item(cx, arg_ty, sym::Vec) + && arg_ty.is_diag_item(cx, sym::Vec) } // This function will check the following: diff --git a/clippy_lints/src/methods/unused_enumerate_index.rs b/clippy_lints/src/methods/unused_enumerate_index.rs index af4ade3cc0f7d..554db52653a35 100644 --- a/clippy_lints/src/methods/unused_enumerate_index.rs +++ b/clippy_lints/src/methods/unused_enumerate_index.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind}; @@ -40,7 +40,7 @@ use crate::loops::UNUSED_ENUMERATE_INDEX; pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>) { let recv_ty = cx.typeck_results().expr_ty(recv); // If we call a method on a `std::iter::Enumerate` instance - if is_type_diagnostic_item(cx, recv_ty, sym::Enumerate) + if recv_ty.is_diag_item(cx, sym::Enumerate) // 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 diff --git a/clippy_lints/src/methods/unwrap_expect_used.rs b/clippy_lints/src/methods/unwrap_expect_used.rs index 027215e3b4d7b..73a407be4f210 100644 --- a/clippy_lints/src/methods/unwrap_expect_used.rs +++ b/clippy_lints/src/methods/unwrap_expect_used.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::{is_never_like, is_type_diagnostic_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::is_never_like; use clippy_utils::{is_in_test, is_inside_always_const_context, is_lint_allowed}; use rustc_hir::Expr; use rustc_lint::{LateContext, Lint}; @@ -45,9 +46,9 @@ pub(super) fn check( ) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - let (kind, none_value, none_prefix) = if is_type_diagnostic_item(cx, ty, sym::Option) && !is_err { + let (kind, none_value, none_prefix) = if ty.is_diag_item(cx, sym::Option) && !is_err { ("an `Option`", "None", "") - } else if is_type_diagnostic_item(cx, ty, sym::Result) + } else if ty.is_diag_item(cx, sym::Result) && let ty::Adt(_, substs) = ty.kind() && let Some(t_or_e_ty) = substs[usize::from(!is_err)].as_type() { diff --git a/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs b/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs index 22df1f3f485e1..c6f54159c7a78 100644 --- a/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs +++ b/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::is_inside_always_const_context; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, Node, QPath, UnsafeSource}; use rustc_lint::LateContext; @@ -15,7 +15,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, func: &Expr<' && segment.ident.name == sym::new_unchecked && let [init_arg] = args && is_inside_always_const_context(cx.tcx, expr.hir_id) - && is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::NonZero) + && cx.typeck_results().node_type(ty.hir_id).is_diag_item(cx, sym::NonZero) && msrv.meets(cx, msrvs::CONST_UNWRAP) { let mut app = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index b0cc7a785bc31..9b670266d0a9c 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -1,4 +1,4 @@ -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::{get_parent_expr, path_to_local_id, usage}; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, QPath, Stmt, StmtKind}; @@ -20,7 +20,7 @@ pub(super) fn derefs_to_slice<'tcx>( match ty.kind() { ty::Slice(_) => true, ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => may_slice(cx, boxed), - ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::Vec), + ty::Adt(..) => ty.is_diag_item(cx, sym::Vec), ty::Array(_, size) => size.try_to_target_usize(cx.tcx).is_some(), ty::Ref(_, inner, _) => may_slice(cx, *inner), _ => false, diff --git a/clippy_lints/src/methods/vec_resize_to_zero.rs b/clippy_lints/src/methods/vec_resize_to_zero.rs index bfb481f4fc092..5debaab2067bd 100644 --- a/clippy_lints/src/methods/vec_resize_to_zero.rs +++ b/clippy_lints/src/methods/vec_resize_to_zero.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -19,7 +19,11 @@ pub(super) fn check<'tcx>( ) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Vec) + && cx + .tcx + .type_of(impl_id) + .instantiate_identity() + .is_diag_item(cx, sym::Vec) && let ExprKind::Lit(Spanned { node: LitKind::Int(Pu128(0), _), .. diff --git a/clippy_lints/src/methods/verbose_file_reads.rs b/clippy_lints/src/methods/verbose_file_reads.rs index 8ed61637eca21..d9927b6e368bd 100644 --- a/clippy_lints/src/methods/verbose_file_reads.rs +++ b/clippy_lints/src/methods/verbose_file_reads.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_trait_method; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -21,7 +21,11 @@ pub(super) fn check<'tcx>( ) { if is_trait_method(cx, expr, sym::IoRead) && matches!(recv.kind, ExprKind::Path(QPath::Resolved(None, _))) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(recv).peel_refs(), sym::File) + && cx + .typeck_results() + .expr_ty_adjusted(recv) + .peel_refs() + .is_diag_item(cx, sym::File) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then(cx, VERBOSE_FILE_READS, expr.span, msg, |diag| { diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs index 1b5671b8d0164..15b773c2c64f4 100644 --- a/clippy_lints/src/missing_fields_in_debug.rs +++ b/clippy_lints/src/missing_fields_in_debug.rs @@ -3,7 +3,6 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::sym; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::{Visitable, for_each_expr}; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; @@ -113,11 +112,9 @@ fn should_lint<'tcx>( if let ExprKind::MethodCall(path, recv, ..) = &expr.kind { let recv_ty = typeck_results.expr_ty(recv).peel_refs(); - if path.ident.name == sym::debug_struct && is_type_diagnostic_item(cx, recv_ty, sym::Formatter) { + if path.ident.name == sym::debug_struct && recv_ty.is_diag_item(cx, sym::Formatter) { has_debug_struct = true; - } else if path.ident.name == sym::finish_non_exhaustive - && is_type_diagnostic_item(cx, recv_ty, sym::DebugStruct) - { + } else if path.ident.name == sym::finish_non_exhaustive && recv_ty.is_diag_item(cx, sym::DebugStruct) { has_finish_non_exhaustive = true; } } @@ -138,7 +135,7 @@ fn as_field_call<'tcx>( ) -> Option { if let ExprKind::MethodCall(path, recv, [debug_field, _], _) = &expr.kind && let recv_ty = typeck_results.expr_ty(recv).peel_refs() - && is_type_diagnostic_item(cx, recv_ty, sym::DebugStruct) + && recv_ty.is_diag_item(cx, sym::DebugStruct) && path.ident.name == sym::field && let ExprKind::Lit(lit) = &debug_field.kind && let LitKind::Str(sym, ..) = lit.node diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index fe2157ca533a6..a93665ef3e9d6 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, IntTy, Ty, UintTy}; @@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let ty = cx.typeck_results().expr_ty(expr); if let ty::Adt(_, subst) = ty.kind() - && is_type_diagnostic_item(cx, ty, sym::Mutex) + && ty.is_diag_item(cx, sym::Mutex) { let mutex_param = subst.type_at(0); if let Some(atomic_name) = get_atomic_name(mutex_param) { diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 455d1426aa8cb..f129b06e4c8ac 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,8 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{ - implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, -}; +use clippy_utils::ty::{implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_lang_item}; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{is_self, path_to_local_id, peel_hir_ty_options, strip_pat_refs, sym}; use rustc_abi::ExternAbi; @@ -219,7 +218,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { diag.span_help(span, "or consider marking this type as `Copy`"); } - if is_type_diagnostic_item(cx, ty, sym::Vec) + if ty.is_diag_item(cx, sym::Vec) && let Some(clone_spans) = get_spans(cx, body, idx, &[(sym::clone, ".to_owned()")]) && let TyKind::Path(QPath::Resolved(_, path)) = input.kind && let Some(elem_ty) = path diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index e062e55dad894..0a6499e095832 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -2,7 +2,7 @@ use super::ARITHMETIC_SIDE_EFFECTS; use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::{expr_or_init, is_from_proc_macro, is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary, sym}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_lint::{LateContext, LateLintPass}; @@ -108,9 +108,7 @@ impl ArithmeticSideEffects { rhs_ty: Ty<'tcx>, ) -> bool { let is_div_or_rem = matches!(op, hir::BinOpKind::Div | hir::BinOpKind::Rem); - let is_sat_or_wrap = |ty: Ty<'_>| { - is_type_diagnostic_item(cx, ty, sym::Saturating) || is_type_diagnostic_item(cx, ty, sym::Wrapping) - }; + let is_sat_or_wrap = |ty: Ty<'_>| ty.is_diag_item(cx, sym::Saturating) || ty.is_diag_item(cx, sym::Wrapping); // If the RHS is `NonZero`, then division or module by zero will never occur. if Self::is_non_zero_u(cx, rhs_ty) && is_div_or_rem { diff --git a/clippy_lints/src/operators/duration_subsec.rs b/clippy_lints/src/operators/duration_subsec.rs index d897b0e8dd918..4a1da7e07a888 100644 --- a/clippy_lints/src/operators/duration_subsec.rs +++ b/clippy_lints/src/operators/duration_subsec.rs @@ -1,8 +1,8 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -18,7 +18,11 @@ pub(crate) fn check<'tcx>( ) { if op == BinOpKind::Div && let ExprKind::MethodCall(method_path, self_arg, [], _) = left.kind - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration) + && cx + .typeck_results() + .expr_ty(self_arg) + .peel_refs() + .is_diag_item(cx, sym::Duration) && let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval_local(right, expr.span.ctxt()) { let suggested_fn = match (method_path.ident.name, divisor) { diff --git a/clippy_lints/src/operators/integer_division.rs b/clippy_lints/src/operators/integer_division.rs index 7b98afa9b40bd..1620312474e99 100644 --- a/clippy_lints/src/operators/integer_division.rs +++ b/clippy_lints/src/operators/integer_division.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::symbol::sym; @@ -16,7 +16,7 @@ pub(crate) fn check<'tcx>( if op == hir::BinOpKind::Div && cx.typeck_results().expr_ty(left).is_integral() && let right_ty = cx.typeck_results().expr_ty(right) - && (right_ty.is_integral() || is_type_diagnostic_item(cx, right_ty, sym::NonZero)) + && (right_ty.is_integral() || right_ty.is_diag_item(cx, sym::NonZero)) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then(cx, INTEGER_DIVISION, expr.span, "integer division", |diag| { diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index ee1d59490ce9f..57127e9d22985 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::visitors::{Descend, for_each_expr}; use clippy_utils::{is_inside_always_const_context, return_ty}; use core::ops::ControlFlow; @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { return; } let owner = cx.tcx.local_def_id_to_hir_id(def_id).expect_owner(); - if is_type_diagnostic_item(cx, return_ty(cx, owner), sym::Result) { + if return_ty(cx, owner).is_diag_item(cx, sym::Result) { lint_impl_body(cx, span, body); } } diff --git a/clippy_lints/src/partialeq_to_none.rs b/clippy_lints/src/partialeq_to_none.rs index 9b9024c810575..e6e4920e7d2c0 100644 --- a/clippy_lints/src/partialeq_to_none.rs +++ b/clippy_lints/src/partialeq_to_none.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::{is_res_lang_ctor, path_res, peel_hir_expr_refs, peel_ref_operators, sugg}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem}; @@ -47,8 +47,12 @@ impl<'tcx> LateLintPass<'tcx> for PartialeqToNone { } // If the expression is of type `Option` - let is_ty_option = - |expr: &Expr<'_>| is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr).peel_refs(), sym::Option); + let is_ty_option = |expr: &Expr<'_>| { + cx.typeck_results() + .expr_ty(expr) + .peel_refs() + .is_diag_item(cx, sym::Option) + }; // If the expression is a literal `Option::None` let is_none_ctor = |expr: &Expr<'_>| { diff --git a/clippy_lints/src/pathbuf_init_then_push.rs b/clippy_lints/src/pathbuf_init_then_push.rs index 4ce6827cac9bb..f561c5c4d3e91 100644 --- a/clippy_lints/src/pathbuf_init_then_push.rs +++ b/clippy_lints/src/pathbuf_init_then_push.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{path_to_local_id, sym}; use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; @@ -137,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { && let PatKind::Binding(BindingMode::MUT, id, name, None) = local.pat.kind && !local.span.in_external_macro(cx.sess().source_map()) && let ty = cx.typeck_results().pat_ty(local.pat) - && is_type_diagnostic_item(cx, ty, sym::PathBuf) + && ty.is_diag_item(cx, sym::PathBuf) { self.searcher = Some(PathbufPushSearcher { local_id: id, @@ -158,7 +158,7 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { && let Res::Local(id) = path.res && !expr.span.in_external_macro(cx.sess().source_map()) && let ty = cx.typeck_results().expr_ty(left) - && is_type_diagnostic_item(cx, ty, sym::PathBuf) + && ty.is_diag_item(cx, sym::PathBuf) { self.searcher = Some(PathbufPushSearcher { local_id: id, diff --git a/clippy_lints/src/permissions_set_readonly_false.rs b/clippy_lints/src/permissions_set_readonly_false.rs index da56a785007c4..68a34d459e0df 100644 --- a/clippy_lints/src/permissions_set_readonly_false.rs +++ b/clippy_lints/src/permissions_set_readonly_false.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::sym; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -34,7 +34,10 @@ impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse { && let ExprKind::Lit(lit) = &arg.kind && LitKind::Bool(false) == lit.node && path.ident.name == sym::set_readonly - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::FsPermissions) + && cx + .typeck_results() + .expr_ty(receiver) + .is_diag_item(cx, sym::FsPermissions) { span_lint_and_then( cx, diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 49c58a276cdd9..8b2ecece8373f 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -4,10 +4,10 @@ use clippy_config::Conf; use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{ eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, @@ -206,7 +206,7 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ IfBlockType::IfIs(caller, caller_ty, call_sym, if_then) => { // If the block could be identified as `if x.is_none()/is_err()`, // we then only need to check the if_then return to see if it is none/err. - is_type_diagnostic_item(cx, caller_ty, smbl) + caller_ty.is_diag_item(cx, smbl) && expr_return_none_or_err(smbl, cx, if_then, caller, None) && match smbl { sym::Option => call_sym == sym::is_none, @@ -215,7 +215,7 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ } }, IfBlockType::IfLet(res, let_expr_ty, let_pat_sym, let_expr, if_then, if_else) => { - is_type_diagnostic_item(cx, let_expr_ty, smbl) + let_expr_ty.is_diag_item(cx, smbl) && match smbl { sym::Option => { // We only need to check `if let Some(x) = option` not `if let None = option`, diff --git a/clippy_lints/src/set_contains_or_insert.rs b/clippy_lints/src/set_contains_or_insert.rs index ff6e6ef214b5b..688da33a17778 100644 --- a/clippy_lints/src/set_contains_or_insert.rs +++ b/clippy_lints/src/set_contains_or_insert.rs @@ -1,7 +1,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::visitors::for_each_expr; use clippy_utils::{SpanlessEq, higher, peel_hir_expr_while, sym}; use rustc_hir::{Expr, ExprKind, UnOp}; @@ -103,7 +103,7 @@ fn try_parse_op_call<'tcx>( let receiver_ty = cx.typeck_results().expr_ty(receiver).peel_refs(); if value.span.eq_ctxt(expr.span) && path.ident.name == symbol { for sym in &[sym::HashSet, sym::BTreeSet] { - if is_type_diagnostic_item(cx, receiver_ty, *sym) { + if receiver_ty.is_diag_item(cx, *sym) { return Some((OpExpr { receiver, value, span }, *sym)); } } diff --git a/clippy_lints/src/single_option_map.rs b/clippy_lints/src/single_option_map.rs index cc497c97a4725..b4375d4f9b042 100644 --- a/clippy_lints/src/single_option_map.rs +++ b/clippy_lints/src/single_option_map.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::{path_res, peel_blocks}; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for SingleOptionMap { if let ExprKind::MethodCall(method_name, callee, args, _span) = func_body.kind && method_name.ident.name == sym::map && let callee_type = cx.typeck_results().expr_ty(callee) - && is_type_diagnostic_item(cx, callee_type, sym::Option) + && callee_type.is_diag_item(cx, sym::Option) && let ExprKind::Path(_path) = callee.kind && let Res::Local(_id) = path_res(cx, callee) && matches!(path_res(cx, callee), Res::Local(_id)) diff --git a/clippy_lints/src/strlen_on_c_strings.rs b/clippy_lints/src/strlen_on_c_strings.rs index 33856c750d7e9..3fdc366d2a7c3 100644 --- a/clippy_lints/src/strlen_on_c_strings.rs +++ b/clippy_lints/src/strlen_on_c_strings.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::is_type_lang_item; use clippy_utils::visitors::is_expr_unsafe; use clippy_utils::{match_libc_symbol, sym}; use rustc_errors::Applicability; @@ -61,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings { let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); let mut app = Applicability::MachineApplicable; let val_name = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0; - let method_name = if is_type_diagnostic_item(cx, ty, sym::cstring_type) { + let method_name = if ty.is_diag_item(cx, sym::cstring_type) { "as_bytes" } else if is_type_lang_item(cx, ty, LangItem::CStr) { "to_bytes" diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index f5400286f8845..319051dce685d 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet_indent, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, path_to_local, std_or_core}; use itertools::Itertools; @@ -110,8 +110,8 @@ fn generate_swap_warning<'tcx>( if matches!(ty.kind(), ty::Slice(_)) || matches!(ty.kind(), ty::Array(_, _)) - || is_type_diagnostic_item(cx, ty, sym::Vec) - || is_type_diagnostic_item(cx, ty, sym::VecDeque) + || ty.is_diag_item(cx, sym::Vec) + || ty.is_diag_item(cx, sym::VecDeque) { let slice = Sugg::hir_with_applicability(cx, lhs1, "", &mut applicability); diff --git a/clippy_lints/src/time_subtraction.rs b/clippy_lints/src/time_subtraction.rs index c25668817f8ba..dbd4ec77fd5f9 100644 --- a/clippy_lints/src/time_subtraction.rs +++ b/clippy_lints/src/time_subtraction.rs @@ -3,7 +3,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -97,16 +96,16 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { let lhs_ty = typeck.expr_ty(lhs); let rhs_ty = typeck.expr_ty(rhs); - if ty::is_type_diagnostic_item(cx, lhs_ty, sym::Instant) { + if lhs_ty.is_diag_item(cx, sym::Instant) { // Instant::now() - instant if is_instant_now_call(cx, lhs) - && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Instant) + && rhs_ty.is_diag_item(cx, sym::Instant) && let Some(sugg) = Sugg::hir_opt(cx, rhs) { print_manual_instant_elapsed_sugg(cx, expr, sugg); } // instant - duration - else if ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) + else if rhs_ty.is_diag_item(cx, sym::Duration) && !expr.span.from_expansion() && self.msrv.meets(cx, msrvs::TRY_FROM) { @@ -123,8 +122,8 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); } } - } else if ty::is_type_diagnostic_item(cx, lhs_ty, sym::Duration) - && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration) + } else if lhs_ty.is_diag_item(cx, sym::Duration) + && rhs_ty.is_diag_item(cx, sym::Duration) && !expr.span.from_expansion() && self.msrv.meets(cx, msrvs::TRY_FROM) { @@ -171,7 +170,7 @@ fn is_chained_time_subtraction(cx: &LateContext<'_>, lhs: &Expr<'_>) -> bool { /// Returns true if the type is Duration or Instant fn is_time_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - ty::is_type_diagnostic_item(cx, ty, sym::Duration) || ty::is_type_diagnostic_item(cx, ty, sym::Instant) + ty.is_diag_item(cx, sym::Duration) || ty.is_diag_item(cx, sym::Instant) } fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) { @@ -195,7 +194,7 @@ fn print_unchecked_duration_subtraction_sugg( let typeck = cx.typeck_results(); let left_ty = typeck.expr_ty(left_expr); - let lint_msg = if ty::is_type_diagnostic_item(cx, left_ty, sym::Instant) { + let lint_msg = if left_ty.is_diag_item(cx, sym::Instant) { "unchecked subtraction of a 'Duration' from an 'Instant'" } else { "unchecked subtraction between 'Duration' values" diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs index 7b90f20b6ba3c..71c4172c98528 100644 --- a/clippy_lints/src/to_digit_is_some.rs +++ b/clippy_lints/src/to_digit_is_some.rs @@ -63,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { } }, hir::ExprKind::Call(to_digits_call, [char_arg, radix_arg]) => { - if to_digits_call.ty_rel_def(cx).is_diag_item(cx, sym::char_to_digit) { + if to_digits_call.res(cx).is_diag_item(cx, sym::char_to_digit) { Some((false, char_arg, radix_arg)) } else { None diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 51116b5eba9e1..9532423b73edc 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; -use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::is_uninit_value_valid_for_ty; use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while, sym}; use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -183,7 +184,10 @@ fn extract_init_or_reserve_target<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt } fn is_reserve(cx: &LateContext<'_>, path: &PathSegment<'_>, self_expr: &Expr<'_>) -> bool { - is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr).peel_refs(), sym::Vec) + cx.typeck_results() + .expr_ty(self_expr) + .peel_refs() + .is_diag_item(cx, sym::Vec) && path.ident.name == sym::reserve } @@ -205,10 +209,7 @@ fn extract_set_len_self<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Opt match expr.kind { ExprKind::MethodCall(path, self_expr, [arg], _) => { let self_type = cx.typeck_results().expr_ty(self_expr).peel_refs(); - if is_type_diagnostic_item(cx, self_type, sym::Vec) - && path.ident.name == sym::set_len - && !is_integer_literal(arg, 0) - { + if self_type.is_diag_item(cx, sym::Vec) && path.ident.name == sym::set_len && !is_integer_literal(arg, 0) { Some((self_expr, expr.span)) } else { None diff --git a/clippy_lints/src/unused_peekable.rs b/clippy_lints/src/unused_peekable.rs index 5224b62e9fc71..5fa16dc5f7a2a 100644 --- a/clippy_lints/src/unused_peekable.rs +++ b/clippy_lints/src/unused_peekable.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::peel_and_count_ty_refs; use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators, sym}; use rustc_ast::Mutability; use rustc_hir::intravisit::{Visitor, walk_expr}; @@ -49,7 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable { // Don't lint `Peekable`s returned from a block if let Some(expr) = block.expr && let Some(ty) = cx.typeck_results().expr_ty_opt(peel_ref_operators(cx, expr)) - && is_type_diagnostic_item(cx, ty, sym::IterPeekable) + && ty.is_diag_item(cx, sym::IterPeekable) { return; } @@ -62,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable { && !init.span.from_expansion() && let Some(ty) = cx.typeck_results().expr_ty_opt(init) && let (ty, _, None | Some(Mutability::Mut)) = peel_and_count_ty_refs(ty) - && is_type_diagnostic_item(cx, ty, sym::IterPeekable) + && ty.is_diag_item(cx, sym::IterPeekable) { let mut vis = PeekableVisitor::new(cx, binding); @@ -212,7 +213,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { fn arg_is_mut_peekable(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool { if let Some(ty) = cx.typeck_results().expr_ty_opt(arg) && let (ty, _, None | Some(Mutability::Mut)) = peel_and_count_ty_refs(ty) - && is_type_diagnostic_item(cx, ty, sym::IterPeekable) + && ty.is_diag_item(cx, sym::IterPeekable) { true } else { diff --git a/clippy_lints/src/unused_result_ok.rs b/clippy_lints/src/unused_result_ok.rs index f5ed10fb76094..fe323a0b7b0c9 100644 --- a/clippy_lints/src/unused_result_ok.rs +++ b/clippy_lints/src/unused_result_ok.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; use clippy_utils::sym; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -37,7 +37,7 @@ impl LateLintPass<'_> for UnusedResultOk { if let StmtKind::Semi(expr) = stmt.kind && let ExprKind::MethodCall(ok_path, recv, [], ..) = expr.kind //check is expr.ok() has type Result.ok(, _) && ok_path.ident.name == sym::ok - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + && cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) && !stmt.span.in_external_macro(cx.sess().source_map()) { let ctxt = expr.span.ctxt(); diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 1b137017ecb46..2954bfea32a67 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; -use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_modulo_regions}; +use clippy_utils::ty::{get_type_diagnostic_name, is_copy, same_type_modulo_regions}; use clippy_utils::{ get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, sym, }; @@ -368,7 +369,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { && name.ident.name == sym::try_into && let a = cx.typeck_results().expr_ty(e) && let b = cx.typeck_results().expr_ty(recv) - && is_type_diagnostic_item(cx, a, sym::Result) + && a.is_diag_item(cx, sym::Result) && let ty::Adt(_, args) = a.kind() && let Some(a_type) = args.types().next() && same_type_modulo_regions(a_type, b) @@ -393,7 +394,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(arg); if name == sym::try_from_fn - && is_type_diagnostic_item(cx, a, sym::Result) + && a.is_diag_item(cx, sym::Result) && let ty::Adt(_, args) = a.kind() && let Some(a_type) = args.types().next() && same_type_modulo_regions(a_type, b) diff --git a/clippy_lints/src/volatile_composites.rs b/clippy_lints/src/volatile_composites.rs index c65b47c0853f4..6402c3ef72c41 100644 --- a/clippy_lints/src/volatile_composites.rs +++ b/clippy_lints/src/volatile_composites.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::res::MaybeDef; use clippy_utils::sym; -use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; @@ -154,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for VolatileComposites { // Raw pointers ty::RawPtr(innerty, _) => report_volatile_safe(cx, expr, *innerty), // std::ptr::NonNull - ty::Adt(_, args) if is_type_diagnostic_item(cx, self_ty, sym::NonNull) => { + ty::Adt(_, args) if self_ty.is_diag_item(cx, sym::NonNull) => { report_volatile_safe(cx, expr, args.type_at(0)); }, _ => (), diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index f1572fd65bbff..bf133d26ed9d0 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{is_type_diagnostic_item, ty_from_hir_ty}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::ty_from_hir_ty; use rustc_hir::{self as hir, AmbigArg, HirId, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf as _; @@ -48,7 +49,7 @@ impl LateLintPass<'_> for ZeroSizedMapValues { && !in_trait_impl(cx, hir_ty.hir_id) // We don't care about infer vars && let ty = ty_from_hir_ty(cx, hir_ty.as_unambig_ty()) - && (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap)) + && (ty.is_diag_item(cx, sym::HashMap) || ty.is_diag_item(cx, sym::BTreeMap)) && let ty::Adt(_, args) = ty.kind() && let ty = args.type_at(1) // Ensure that no type information is missing, to avoid a delayed bug in the compiler if this is not the case. diff --git a/clippy_lints/src/zombie_processes.rs b/clippy_lints/src/zombie_processes.rs index a934d2094e004..1f7ea06475d87 100644 --- a/clippy_lints/src/zombie_processes.rs +++ b/clippy_lints/src/zombie_processes.rs @@ -1,6 +1,6 @@ use ControlFlow::{Break, Continue}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::res::MaybeDef; use clippy_utils::{fn_def_id, get_enclosing_block, path_to_local_id}; use rustc_ast::Mutability; use rustc_ast::visit::visit_opt; @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for ZombieProcesses { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::Call(..) | ExprKind::MethodCall(..) = expr.kind && let child_ty = cx.typeck_results().expr_ty(expr) - && is_type_diagnostic_item(cx, child_ty, sym::Child) + && child_ty.is_diag_item(cx, sym::Child) { match cx.tcx.parent_hir_node(expr.hir_id) { Node::LetStmt(local) diff --git a/clippy_lints_internal/src/unnecessary_def_path.rs b/clippy_lints_internal/src/unnecessary_def_path.rs index 8877f1faf0ee8..ced0a4b067bb9 100644 --- a/clippy_lints_internal/src/unnecessary_def_path.rs +++ b/clippy_lints_internal/src/unnecessary_def_path.rs @@ -26,7 +26,7 @@ declare_tool_lint! { /// /// Use instead: /// ```rust,ignore - /// is_type_diagnostic_item(cx, ty, sym::Vec) + /// ty.is_diag_item(cx, sym::Vec) /// ``` pub clippy::UNNECESSARY_DEF_PATH, Warn, diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 9fe6d8c62d2a5..3383e66fe3688 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -3,7 +3,7 @@ #![deny(clippy::missing_docs_in_private_items)] use crate::consts::{ConstEvalCtxt, Constant}; -use crate::ty::is_type_diagnostic_item; +use crate::res::MaybeDef; use crate::{is_expn_of, sym}; use rustc_ast::ast; @@ -453,7 +453,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - if let ExprKind::Call(func, args) = expr.kind { match func.kind { ExprKind::Path(QPath::TypeRelative(ty, name)) - if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec) => + if cx.typeck_results().node_type(ty.hir_id).is_diag_item(cx, sym::Vec) => { if name.ident.name == sym::new { return Some(VecInitKind::New); @@ -469,7 +469,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - }, ExprKind::Path(QPath::Resolved(_, path)) if cx.tcx.is_diagnostic_item(sym::default_fn, path.res.opt_def_id()?) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) => + && cx.typeck_results().expr_ty(expr).is_diag_item(cx, sym::Vec) => { return Some(VecInitKind::Default); }, diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index c48b5b7c171ff..e5beac035ca4c 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -36,6 +36,7 @@ use std::{iter, mem}; use crate::path_res; use crate::paths::{PathNS, lookup_path_str}; +use crate::res::MaybeDef; mod type_certainty; pub use type_certainty::expr_type_is_certain; @@ -381,26 +382,8 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { /// Checks if the type is a reference equals to a diagnostic item pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { - match ty.kind() { - ty::Ref(_, ref_ty, _) => is_type_diagnostic_item(cx, *ref_ty, diag_item), - _ => false, - } -} - -/// Checks if the type is equal to a diagnostic item. To check if a type implements a -/// trait marked with a diagnostic item use [`implements_trait`]. -/// -/// For a further exploitation what diagnostic items are see [diagnostic items] in -/// rustc-dev-guide. -/// -/// --- -/// -/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem` -/// -/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html -pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { - match ty.kind() { - ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()), + match *ty.kind() { + ty::Ref(_, ref_ty, _) => ref_ty.is_diag_item(cx, diag_item), _ => false, } } @@ -1298,11 +1281,14 @@ pub fn get_field_by_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, name: Symbol) -> /// Check if `ty` is an `Option` and return its argument type if it is. pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - match ty.kind() { - ty::Adt(adt, args) => cx - .tcx - .is_diagnostic_item(sym::Option, adt.did()) - .then(|| args.type_at(0)), + match *ty.kind() { + ty::Adt(adt, args) + if let [arg] = &**args + && let Some(arg) = arg.as_type() + && adt.is_diag_item(cx, sym::Option) => + { + Some(arg) + }, _ => None, } } @@ -1356,7 +1342,7 @@ pub fn has_non_owning_mutable_access<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<' /// Check if `ty` is slice-like, i.e., `&[T]`, `[T; N]`, or `Vec`. pub fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_slice() || ty.is_array() || is_type_diagnostic_item(cx, ty, sym::Vec) + ty.is_slice() || ty.is_array() || ty.is_diag_item(cx, sym::Vec) } pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option { From fe13e0675a3ab92395ef9ac82445f21d295b4815 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 05:57:32 -0400 Subject: [PATCH 50/89] Remove `is_type_ref_to_diagnostic_item` --- clippy_utils/src/ty/mod.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index e5beac035ca4c..47cd5a0f69cd1 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -380,14 +380,6 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { } } -/// Checks if the type is a reference equals to a diagnostic item -pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { - match *ty.kind() { - ty::Ref(_, ref_ty, _) => ref_ty.is_diag_item(cx, diag_item), - _ => false, - } -} - /// Checks if the type is equal to a lang item. /// /// Returns `false` if the `LangItem` is not defined. From 083b1c1059553121a700969d4f55e5d0b21e8a0b Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 05:58:13 -0400 Subject: [PATCH 51/89] Remove `is_type_lang_item` --- book/src/development/common_tools_writing_lints.md | 2 +- clippy_lints/src/collection_is_never_read.rs | 5 +++-- clippy_lints/src/drop_forget_ref.rs | 5 +++-- clippy_lints/src/format_args.rs | 5 +++-- clippy_lints/src/format_push_string.rs | 7 +++++-- clippy_lints/src/from_str_radix_10.rs | 4 ++-- clippy_lints/src/inherent_to_string.rs | 5 +++-- .../src/loops/char_indices_as_byte_indices.rs | 4 ++-- clippy_lints/src/loops/explicit_iter_loop.rs | 6 +++--- clippy_lints/src/manual_ignore_case_cmp.rs | 4 ++-- clippy_lints/src/manual_retain.rs | 5 +++-- clippy_lints/src/matches/match_str_case_mismatch.rs | 4 ++-- clippy_lints/src/methods/bytes_count_to_len.rs | 4 ++-- clippy_lints/src/methods/bytes_nth.rs | 4 ++-- .../case_sensitive_file_extension_comparisons.rs | 4 ++-- clippy_lints/src/methods/clear_with_drain.rs | 3 +-- clippy_lints/src/methods/drain_collect.rs | 6 +++--- clippy_lints/src/methods/expect_fun_call.rs | 3 +-- clippy_lints/src/methods/extend_with_drain.rs | 3 +-- clippy_lints/src/methods/format_collect.rs | 4 ++-- clippy_lints/src/methods/inefficient_to_string.rs | 5 +++-- clippy_lints/src/methods/manual_str_repeat.rs | 12 +++++++----- clippy_lints/src/methods/needless_as_bytes.rs | 4 ++-- clippy_lints/src/methods/no_effect_replace.rs | 4 ++-- clippy_lints/src/methods/repeat_once.rs | 4 ++-- clippy_lints/src/methods/search_is_some.rs | 4 ++-- clippy_lints/src/methods/sliced_string_as_bytes.rs | 4 ++-- clippy_lints/src/methods/string_extend_chars.rs | 6 +++--- clippy_lints/src/methods/unnecessary_join.rs | 6 +++--- clippy_lints/src/methods/unnecessary_to_owned.rs | 6 +++--- clippy_lints/src/needless_pass_by_value.rs | 4 ++-- clippy_lints/src/redundant_clone.rs | 5 +++-- clippy_lints/src/redundant_slicing.rs | 8 ++++++-- clippy_lints/src/strings.rs | 9 ++++++--- clippy_lints/src/strlen_on_c_strings.rs | 3 +-- clippy_lints/src/unnecessary_owned_empty_strings.rs | 4 ++-- clippy_utils/src/ty/mod.rs | 12 +----------- 37 files changed, 96 insertions(+), 91 deletions(-) diff --git a/book/src/development/common_tools_writing_lints.md b/book/src/development/common_tools_writing_lints.md index 74fc788e9e3ae..7fde4cb408a5e 100644 --- a/book/src/development/common_tools_writing_lints.md +++ b/book/src/development/common_tools_writing_lints.md @@ -161,7 +161,7 @@ paths for Clippy can be found in [paths.rs][paths] To check if our type defines a method called `some_method`: ```rust -use clippy_utils::res::MaybeDef; +use clippy_utils::ty::is_type_lang_item; use clippy_utils::{sym, return_ty}; impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { diff --git a/clippy_lints/src/collection_is_never_read.rs b/clippy_lints/src/collection_is_never_read.rs index 1279be34ed8f2..e020f9e2ec5ba 100644 --- a/clippy_lints/src/collection_is_never_read.rs +++ b/clippy_lints/src/collection_is_never_read.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::visitors::{Visitable, for_each_expr}; use clippy_utils::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; @@ -71,7 +72,7 @@ fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool { | sym::Vec | sym::VecDeque ) - ) || is_type_lang_item(cx, ty, LangItem::String) + ) || ty.is_lang_item(cx, LangItem::String) } fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirId, block: T) -> bool { diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 5c360ce6a5f7e..3bb8c484ceec3 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_must_use_func_call; -use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::{is_copy, is_must_use_ty}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -97,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { sym::mem_forget if arg_ty.is_ref() => return, sym::mem_drop if is_copy && !drop_is_single_call_in_arm => return, sym::mem_forget if is_copy => return, - sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => return, + sym::mem_drop if arg_ty.is_lang_item(cx, LangItem::ManuallyDrop) => return, sym::mem_drop if !(arg_ty.needs_drop(cx.tcx, cx.typing_env()) || is_must_use_func_call(cx, arg) diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 35965f4977cfc..d7a9dd0d008a6 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -9,8 +9,9 @@ use clippy_utils::macros::{ root_macro_call_first_node, }; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{implements_trait, is_type_lang_item}; +use clippy_utils::ty::implements_trait; use clippy_utils::{is_diag_trait_item, is_from_proc_macro, is_in_test, trait_ref_of_method}; use itertools::Itertools; use rustc_ast::{ @@ -344,7 +345,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { if let Some(placeholder_span) = placeholder.span && *options != FormatOptions::default() && let ty = self.cx.typeck_results().expr_ty(arg).peel_refs() - && is_type_lang_item(self.cx, ty, LangItem::FormatArguments) + && ty.is_lang_item(self.cx, LangItem::FormatArguments) { span_lint_and_then( self.cx, diff --git a/clippy_lints/src/format_push_string.rs b/clippy_lints/src/format_push_string.rs index b64d608c0c709..a23ba9ab837ae 100644 --- a/clippy_lints/src/format_push_string.rs +++ b/clippy_lints/src/format_push_string.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use rustc_hir::{AssignOpKind, Expr, ExprKind, LangItem, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -41,7 +41,10 @@ declare_clippy_lint! { declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]); fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - is_type_lang_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), LangItem::String) + cx.typeck_results() + .expr_ty(e) + .peel_refs() + .is_lang_item(cx, LangItem::String) } fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { let e = e.peel_blocks().peel_borrows(); diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index d5873b3f85aa1..df8a35d9658b0 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::{is_in_const_context, is_integer_literal, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, PrimTy, QPath, TyKind, def}; @@ -89,5 +89,5 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { /// Checks if a Ty is `String` or `&str` fn is_ty_stringish(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_lang_item(cx, ty, LangItem::String) || ty.peel_refs().is_str() + ty.is_lang_item(cx, LangItem::String) || ty.peel_refs().is_str() } diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index 7f2e25367a6a4..e569a5c7b6126 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{implements_trait, is_type_lang_item}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::implements_trait; use clippy_utils::{return_ty, trait_ref_of_method}; use rustc_abi::ExternAbi; use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind, LangItem}; @@ -104,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { && impl_item.generics.params.iter().all(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) && !impl_item.span.from_expansion() // Check if return type is String - && is_type_lang_item(cx, return_ty(cx, impl_item.owner_id), LangItem::String) + && return_ty(cx, impl_item.owner_id).is_lang_item(cx, LangItem::String) // Filters instances of to_string which are required by a trait && trait_ref_of_method(cx, impl_item.owner_id).is_none() { diff --git a/clippy_lints/src/loops/char_indices_as_byte_indices.rs b/clippy_lints/src/loops/char_indices_as_byte_indices.rs index a702e60f1c276..f2c87a2863c5f 100644 --- a/clippy_lints/src/loops/char_indices_as_byte_indices.rs +++ b/clippy_lints/src/loops/char_indices_as_byte_indices.rs @@ -1,7 +1,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use clippy_utils::visitors::for_each_expr; use clippy_utils::{eq_expr_value, higher, path_to_local_id, sym}; use rustc_errors::{Applicability, MultiSpan}; @@ -81,7 +81,7 @@ fn check_index_usage<'tcx>( return; }; - let is_string_like = |ty: Ty<'_>| ty.is_str() || is_type_lang_item(cx, ty, LangItem::String); + let is_string_like = |ty: Ty<'_>| ty.is_str() || ty.is_lang_item(cx, LangItem::String); let message = match parent_expr.kind { ExprKind::MethodCall(segment, recv, ..) // We currently only lint `str` methods (which `String` can deref to), so a `.is_str()` check is sufficient here diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index af475c40586fe..40d1d36bd162c 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -1,10 +1,11 @@ use super::EXPLICIT_ITER_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; use clippy_utils::sym; use clippy_utils::ty::{ - implements_trait, implements_trait_with_env, is_copy, is_type_lang_item, make_normalized_projection, + implements_trait, implements_trait_with_env, is_copy, make_normalized_projection, make_normalized_projection_with_regions, normalize_with_regions, }; use rustc_errors::Applicability; @@ -127,8 +128,7 @@ fn is_ref_iterable<'tcx>( let self_ty = typeck.expr_ty(self_arg); let self_is_copy = is_copy(cx, self_ty); - if is_type_lang_item(cx, self_ty.peel_refs(), rustc_hir::LangItem::OwnedBox) - && !msrv.meets(cx, msrvs::BOX_INTO_ITER) + if self_ty.peel_refs().is_lang_item(cx, rustc_hir::LangItem::OwnedBox) && !msrv.meets(cx, msrvs::BOX_INTO_ITER) { return None; } diff --git a/clippy_lints/src/manual_ignore_case_cmp.rs b/clippy_lints/src/manual_ignore_case_cmp.rs index ae17b4e446e4c..18beff5b87091 100644 --- a/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/clippy_lints/src/manual_ignore_case_cmp.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; -use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item}; +use clippy_utils::ty::get_type_diagnostic_name; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::ExprKind::{Binary, Lit, MethodCall}; @@ -74,7 +74,7 @@ fn needs_ref_to_cmp(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { ty.is_char() || *ty.kind() == ty::Uint(UintTy::U8) || ty.is_diag_item(cx, sym::Vec) - || is_type_lang_item(cx, ty, LangItem::String) + || ty.is_lang_item(cx, LangItem::String) } impl LateLintPass<'_> for ManualIgnoreCaseCmp { diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index 7fb88763e640f..672a4c2676269 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -2,8 +2,9 @@ use clippy_config::Conf; use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item}; +use clippy_utils::ty::get_type_diagnostic_name; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::ExprKind::Assign; @@ -189,7 +190,7 @@ fn check_to_owned( && let Some(chars_expr_def_id) = cx.typeck_results().type_dependent_def_id(chars_expr.hir_id) && cx.tcx.is_diagnostic_item(sym::str_chars, chars_expr_def_id) && let ty = cx.typeck_results().expr_ty(str_expr).peel_refs() - && is_type_lang_item(cx, ty, hir::LangItem::String) + && ty.is_lang_item(cx, hir::LangItem::String) && SpanlessEq::new(cx).eq_expr(left_expr, str_expr) && let hir::ExprKind::MethodCall(_, _, [closure_expr], _) = filter_expr.kind && let hir::ExprKind::Closure(closure) = closure_expr.kind diff --git a/clippy_lints/src/matches/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs index eb8b16e1561bd..3f8f2dc0e132d 100644 --- a/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -1,8 +1,8 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::sym; -use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr}; @@ -58,7 +58,7 @@ impl MatchExprVisitor<'_, '_> { if let Some(case_method) = get_case_method(segment_ident) { let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs(); - if is_type_lang_item(self.cx, ty, LangItem::String) || ty.kind() == &ty::Str { + if ty.is_lang_item(self.cx, LangItem::String) || ty.kind() == &ty::Str { return ControlFlow::Break(case_method); } } diff --git a/clippy_lints/src/methods/bytes_count_to_len.rs b/clippy_lints/src/methods/bytes_count_to_len.rs index b8cc5ddd845c9..baea49296cd7a 100644 --- a/clippy_lints/src/methods/bytes_count_to_len.rs +++ b/clippy_lints/src/methods/bytes_count_to_len.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>( && let Some(impl_id) = cx.tcx.impl_of_assoc(bytes_id) && cx.tcx.type_of(impl_id).instantiate_identity().is_str() && let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs() - && (ty.is_str() || is_type_lang_item(cx, ty, hir::LangItem::String)) + && (ty.is_str() || ty.is_lang_item(cx, hir::LangItem::String)) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs index 02fc09170e59c..40d521d61c118 100644 --- a/clippy_lints/src/methods/bytes_nth.rs +++ b/clippy_lints/src/methods/bytes_nth.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E let ty = cx.typeck_results().expr_ty(recv).peel_refs(); let caller_type = if ty.is_str() { "str" - } else if is_type_lang_item(cx, ty, LangItem::String) { + } else if ty.is_lang_item(cx, LangItem::String) { "String" } else { return; diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index 6f9702f6c6c33..b4b10e972f6d4 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; use clippy_utils::sym; -use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; @@ -43,7 +43,7 @@ pub(super) fn check<'tcx>( || ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit())) && !ext_str.chars().skip(1).all(|c| c.is_ascii_digit()) && let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs() - && (recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String)) + && (recv_ty.is_str() || recv_ty.is_lang_item(cx, LangItem::String)) { span_lint_and_then( cx, diff --git a/clippy_lints/src/methods/clear_with_drain.rs b/clippy_lints/src/methods/clear_with_drain.rs index 77b1ec3f97883..67def8767bb0a 100644 --- a/clippy_lints/src/methods/clear_with_drain.rs +++ b/clippy_lints/src/methods/clear_with_drain.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_range_full; use clippy_utils::res::MaybeDef; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, QPath}; use rustc_lint::LateContext; @@ -32,7 +31,7 @@ fn match_acceptable_type(cx: &LateContext<'_>, expr: &Expr<'_>, types: &[rustc_s let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs(); types.iter().any(|&ty| expr_ty.is_diag_item(cx, ty)) // String type is a lang item but not a diagnostic item for now so we need a separate check - || is_type_lang_item(cx, expr_ty, LangItem::String) + || expr_ty.is_lang_item(cx, LangItem::String) } fn suggest(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span) { diff --git a/clippy_lints/src/methods/drain_collect.rs b/clippy_lints/src/methods/drain_collect.rs index cbf713a3b17c4..9b0c290577405 100644 --- a/clippy_lints/src/methods/drain_collect.rs +++ b/clippy_lints/src/methods/drain_collect.rs @@ -1,7 +1,7 @@ use crate::methods::DRAIN_COLLECT; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::{is_range_full, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath}; @@ -35,8 +35,8 @@ fn check_vec(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_> /// Checks `std::string::String` fn check_string(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_>, recv_path: &Path<'_>) -> bool { - is_type_lang_item(cx, expr, LangItem::String) - && is_type_lang_item(cx, recv, LangItem::String) + expr.is_lang_item(cx, LangItem::String) + && recv.is_lang_item(cx, LangItem::String) && matches!(args, [arg] if is_range_full(cx, arg, Some(recv_path))) } diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index ea49fd414457d..288f966991acb 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span, root_macro_call_first_node}; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::visitors::for_each_expr; use clippy_utils::{contains_return, is_inside_always_const_context, peel_blocks}; use rustc_errors::Applicability; @@ -84,7 +83,7 @@ fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Ex if (method_name.ident.name == sym::as_str || method_name.ident.name == sym::as_ref) && { let arg_type = cx.typeck_results().expr_ty(receiver); let base_type = arg_type.peel_refs(); - base_type.is_str() || is_type_lang_item(cx, base_type, hir::LangItem::String) + base_type.is_str() || base_type.is_lang_item(cx, hir::LangItem::String) } { receiver } else { diff --git a/clippy_lints/src/methods/extend_with_drain.rs b/clippy_lints/src/methods/extend_with_drain.rs index 381e86173f85c..829c1226a859e 100644 --- a/clippy_lints/src/methods/extend_with_drain.rs +++ b/clippy_lints/src/methods/extend_with_drain.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; @@ -22,7 +21,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: && src_ty.is_diag_item(cx, sym::Vec) //check drain range && let src_ty_range = cx.typeck_results().expr_ty(drain_arg).peel_refs() - && is_type_lang_item(cx, src_ty_range, LangItem::RangeFull) + && src_ty_range.is_lang_item(cx, LangItem::RangeFull) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/clippy_lints/src/methods/format_collect.rs b/clippy_lints/src/methods/format_collect.rs index 1b28596d50da6..7b2fe00d32188 100644 --- a/clippy_lints/src/methods/format_collect.rs +++ b/clippy_lints/src/methods/format_collect.rs @@ -1,7 +1,7 @@ use super::FORMAT_COLLECT; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_format_macro, root_macro_call_first_node}; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; use rustc_span::Span; @@ -17,7 +17,7 @@ fn peel_non_expn_blocks<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx> } pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, map_arg: &Expr<'_>, map_span: Span) { - if is_type_lang_item(cx, cx.typeck_results().expr_ty(expr), LangItem::String) + if cx.typeck_results().expr_ty(expr).is_lang_item(cx, LangItem::String) && let ExprKind::Closure(closure) = map_arg.kind && let body = cx.tcx.hir_body(closure.body) && let Some(value) = peel_non_expn_blocks(body.value) diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 1b350b7abb619..7a49a5f31f035 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs}; +use clippy_utils::ty::peel_and_count_ty_refs; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -51,7 +52,7 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { return true; } - if is_type_lang_item(cx, ty, hir::LangItem::String) { + if ty.is_lang_item(cx, hir::LangItem::String) { return true; } diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index a7052040c7c05..4fe14f8053c94 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_lang_item; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; @@ -35,14 +34,14 @@ fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { } } else { let ty = cx.typeck_results().expr_ty(e); - if is_type_lang_item(cx, ty, LangItem::String) - || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).is_some_and(Ty::is_str)) + if ty.is_lang_item(cx, LangItem::String) + || (ty.is_lang_item(cx, LangItem::OwnedBox) && get_ty_param(ty).is_some_and(Ty::is_str)) || (ty.is_diag_item(cx, sym::Cow) && get_ty_param(ty).is_some_and(Ty::is_str)) { Some(RepeatKind::String) } else { let ty = ty.peel_refs(); - (ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)).then_some(RepeatKind::String) + (ty.is_str() || ty.is_lang_item(cx, LangItem::String)).then_some(RepeatKind::String) } } } @@ -56,7 +55,10 @@ pub(super) fn check( ) { if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind && repeat_fn.basic_res().is_diag_item(cx, sym::iter_repeat) - && is_type_lang_item(cx, cx.typeck_results().expr_ty(collect_expr), LangItem::String) + && cx + .typeck_results() + .expr_ty(collect_expr) + .is_lang_item(cx, LangItem::String) && let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id) && let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator) && cx.tcx.trait_of_assoc(take_id) == Some(iter_trait_id) diff --git a/clippy_lints/src/methods/needless_as_bytes.rs b/clippy_lints/src/methods/needless_as_bytes.rs index 635d06330e058..22baad40b4496 100644 --- a/clippy_lints/src/methods/needless_as_bytes.rs +++ b/clippy_lints/src/methods/needless_as_bytes.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; @@ -10,7 +10,7 @@ use super::NEEDLESS_AS_BYTES; pub fn check(cx: &LateContext<'_>, prev_method: Symbol, method: Symbol, prev_recv: &Expr<'_>, span: Span) { let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs(); - if is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str() { + if ty1.is_lang_item(cx, LangItem::String) || ty1.is_str() { let mut app = Applicability::MachineApplicable; let sugg = Sugg::hir_with_context(cx, prev_recv, span.ctxt(), "..", &mut app); span_lint_and_sugg( diff --git a/clippy_lints/src/methods/no_effect_replace.rs b/clippy_lints/src/methods/no_effect_replace.rs index 32f32f1b21673..9fa51f78c99d7 100644 --- a/clippy_lints/src/methods/no_effect_replace.rs +++ b/clippy_lints/src/methods/no_effect_replace.rs @@ -1,6 +1,6 @@ use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use rustc_ast::LitKind; use rustc_hir::{ExprKind, LangItem}; use rustc_lint::LateContext; @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>( arg2: &'tcx rustc_hir::Expr<'_>, ) { let ty = cx.typeck_results().expr_ty(expr).peel_refs(); - if !(ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)) { + if !(ty.is_str() || ty.is_lang_item(cx, LangItem::String)) { return; } diff --git a/clippy_lints/src/methods/repeat_once.rs b/clippy_lints/src/methods/repeat_once.rs index 9111604ef53b7..57a7254605f93 100644 --- a/clippy_lints/src/methods/repeat_once.rs +++ b/clippy_lints/src/methods/repeat_once.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; @@ -36,7 +36,7 @@ pub(super) fn check<'tcx>( format!("{}.to_vec()", snippet(cx, recv.span, r#""...""#)), Applicability::MachineApplicable, ); - } else if is_type_lang_item(cx, ty, LangItem::String) { + } else if ty.is_lang_item(cx, LangItem::String) { span_lint_and_sugg( cx, REPEAT_ONCE, diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 1478bc29aef51..95f7a3ebbd300 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::deref_closure_args; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::{is_receiver_of_method_call, is_trait_method, strip_pat_refs, sym}; use hir::ExprKind; use rustc_errors::Applicability; @@ -109,7 +109,7 @@ pub(super) fn check<'tcx>( else if search_method == sym::find { let is_string_or_str_slice = |e| { let self_ty = cx.typeck_results().expr_ty(e).peel_refs(); - if is_type_lang_item(cx, self_ty, hir::LangItem::String) { + if self_ty.is_lang_item(cx, hir::LangItem::String) { true } else { self_ty.is_str() diff --git a/clippy_lints/src/methods/sliced_string_as_bytes.rs b/clippy_lints/src/methods/sliced_string_as_bytes.rs index 6d4cfdb34f319..4aff194923a6c 100644 --- a/clippy_lints/src/methods/sliced_string_as_bytes.rs +++ b/clippy_lints/src/methods/sliced_string_as_bytes.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, is_range_literal}; use rustc_lint::LateContext; @@ -11,7 +11,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) { if let ExprKind::Index(indexed, index, _) = recv.kind && is_range_literal(index) && let ty = cx.typeck_results().expr_ty(indexed).peel_refs() - && (ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)) + && (ty.is_str() || ty.is_lang_item(cx, LangItem::String)) { let mut applicability = Applicability::MaybeIncorrect; let stringish = snippet_with_applicability(cx, indexed.span, "_", &mut applicability); diff --git a/clippy_lints/src/methods/string_extend_chars.rs b/clippy_lints/src/methods/string_extend_chars.rs index f11a41f90f1ab..1fc0633f4f959 100644 --- a/clippy_lints/src/methods/string_extend_chars.rs +++ b/clippy_lints/src/methods/string_extend_chars.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::{method_chain_args, sym}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -10,7 +10,7 @@ use super::STRING_EXTEND_CHARS; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if !is_type_lang_item(cx, obj_ty, hir::LangItem::String) { + if !obj_ty.is_lang_item(cx, hir::LangItem::String) { return; } if let Some(arglists) = method_chain_args(arg, &[sym::chars]) { @@ -22,7 +22,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr } else { "" } - } else if is_type_lang_item(cx, self_ty, hir::LangItem::String) { + } else if self_ty.is_lang_item(cx, hir::LangItem::String) { "&" } else { return; diff --git a/clippy_lints/src/methods/unnecessary_join.rs b/clippy_lints/src/methods/unnecessary_join.rs index efd1a718504ce..3290bdd778243 100644 --- a/clippy_lints/src/methods/unnecessary_join.rs +++ b/clippy_lints/src/methods/unnecessary_join.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; @@ -20,8 +20,8 @@ pub(super) fn check<'tcx>( let collect_output_adjusted_type = cx.typeck_results().expr_ty_adjusted(join_self_arg); if let ty::Ref(_, ref_type, _) = collect_output_adjusted_type.kind() // the turbofish for collect is ::> - && let ty::Slice(slice) = ref_type.kind() - && is_type_lang_item(cx, *slice, LangItem::String) + && let ty::Slice(slice) = *ref_type.kind() + && slice.is_lang_item(cx, LangItem::String) // the argument for join is "" && let ExprKind::Lit(spanned) = &join_arg.kind && let LitKind::Str(symbol, _) = spanned.node diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index e57cfae88f1f3..768b286ea1326 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_lang_item, peel_and_count_ty_refs}; +use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_and_count_ty_refs}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, return_ty, sym, @@ -319,7 +319,7 @@ fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symb // We may end-up here because of an expression like `x.to_string().split(…)` where the type of `x` // implements `AsRef` but does not implement `Deref`. In this case, we have to // add `.as_ref()` to the suggestion. - let as_ref = if is_type_lang_item(cx, cx.typeck_results().expr_ty(expr), LangItem::String) + let as_ref = if cx.typeck_results().expr_ty(expr).is_lang_item(cx, LangItem::String) && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) && cx.get_associated_type(cx.typeck_results().expr_ty(receiver), deref_trait_id, sym::Target) != Some(cx.tcx.types.str_) @@ -672,7 +672,7 @@ fn std_map_key<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { } fn is_str_and_string(cx: &LateContext<'_>, arg_ty: Ty<'_>, original_arg_ty: Ty<'_>) -> bool { - original_arg_ty.is_str() && is_type_lang_item(cx, arg_ty, LangItem::String) + original_arg_ty.is_str() && arg_ty.is_lang_item(cx, LangItem::String) } fn is_slice_and_vec(cx: &LateContext<'_>, arg_ty: Ty<'_>, original_arg_ty: Ty<'_>) -> bool { diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index f129b06e4c8ac..6c578d39bacbd 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_lang_item}; +use clippy_utils::ty::{implements_trait, implements_trait_with_env_from_iter, is_copy}; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{is_self, path_to_local_id, peel_hir_ty_options, strip_pat_refs, sym}; use rustc_abi::ExternAbi; @@ -261,7 +261,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { return; } - if is_type_lang_item(cx, ty, LangItem::String) + if ty.is_lang_item(cx, LangItem::String) && let Some(clone_spans) = get_spans(cx, body, idx, &[(sym::clone, ".to_string()"), (sym::as_str, "")]) { diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index de6766cbe94aa..13c1b10bf2e8c 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::fn_has_unsatisfiable_preds; use clippy_utils::mir::{LocalUsage, PossibleBorrowerMap, visit_local_usage}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::{has_drop, is_copy, is_type_lang_item, peel_and_count_ty_refs}; +use clippy_utils::ty::{has_drop, is_copy, peel_and_count_ty_refs}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, LangItem, def_id}; @@ -100,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let from_borrow = cx.tcx.lang_items().get(LangItem::CloneFn) == Some(fn_def_id) || fn_name == Some(sym::to_owned_method) - || (fn_name == Some(sym::to_string_method) && is_type_lang_item(cx, arg_ty, LangItem::String)); + || (fn_name == Some(sym::to_string_method) && arg_ty.is_lang_item(cx, LangItem::String)); let from_deref = !from_borrow && matches!(fn_name, Some(sym::path_to_pathbuf | sym::os_str_to_os_string)); diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index a358eff2ce554..f2cf809d6012a 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_parent_expr; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs}; +use clippy_utils::ty::peel_and_count_ty_refs; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; @@ -80,7 +81,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind && addressee.span.ctxt() == ctxt && let ExprKind::Index(indexed, range, _) = addressee.kind - && is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull) + && cx + .typeck_results() + .expr_ty_adjusted(range) + .is_lang_item(cx, LangItem::RangeFull) { let (expr_ty, expr_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(expr)); let (indexed_ty, indexed_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(indexed)); diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 57d5900b045ea..c70227cefbc73 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::{ SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, method_calls, path_def_id, peel_blocks, sym, @@ -188,7 +188,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { }, ExprKind::Index(target, _idx, _) => { let e_ty = cx.typeck_results().expr_ty_adjusted(target).peel_refs(); - if e_ty.is_str() || is_type_lang_item(cx, e_ty, LangItem::String) { + if e_ty.is_str() || e_ty.is_lang_item(cx, LangItem::String) { span_lint( cx, STRING_SLICE, @@ -203,7 +203,10 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { } fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - is_type_lang_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), LangItem::String) + cx.typeck_results() + .expr_ty(e) + .peel_refs() + .is_lang_item(cx, LangItem::String) } fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { diff --git a/clippy_lints/src/strlen_on_c_strings.rs b/clippy_lints/src/strlen_on_c_strings.rs index 3fdc366d2a7c3..58d692db5029d 100644 --- a/clippy_lints/src/strlen_on_c_strings.rs +++ b/clippy_lints/src/strlen_on_c_strings.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::visitors::is_expr_unsafe; use clippy_utils::{match_libc_symbol, sym}; use rustc_errors::Applicability; @@ -64,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings { let val_name = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0; let method_name = if ty.is_diag_item(cx, sym::cstring_type) { "as_bytes" - } else if is_type_lang_item(cx, ty, LangItem::CStr) { + } else if ty.is_lang_item(cx, LangItem::CStr) { "to_bytes" } else { return; diff --git a/clippy_lints/src/unnecessary_owned_empty_strings.rs b/clippy_lints/src/unnecessary_owned_empty_strings.rs index 28f4884fa3110..0388450c9f7e2 100644 --- a/clippy_lints/src/unnecessary_owned_empty_strings.rs +++ b/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_lang_item; +use clippy_utils::res::MaybeDef; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; @@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { && let LitKind::Str(symbol, _) = spanned.node && symbol.is_empty() && let inner_expr_type = cx.typeck_results().expr_ty(inner_expr) - && is_type_lang_item(cx, inner_expr_type, LangItem::String) + && inner_expr_type.is_lang_item(cx, LangItem::String) { span_lint_and_sugg( cx, diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 47cd5a0f69cd1..fb07f6c240381 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -380,16 +380,6 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { } } -/// Checks if the type is equal to a lang item. -/// -/// Returns `false` if the `LangItem` is not defined. -pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: LangItem) -> bool { - match ty.kind() { - ty::Adt(adt, _) => cx.tcx.lang_items().get(lang_item) == Some(adt.did()), - _ => false, - } -} - /// Return `true` if the passed `typ` is `isize` or `usize`. pub fn is_isize_or_usize(typ: Ty<'_>) -> bool { matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) @@ -408,7 +398,7 @@ pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { false } // Check for std types which implement drop, but only for memory allocation. - else if is_type_lang_item(cx, ty, LangItem::OwnedBox) + else if ty.is_lang_item(cx, LangItem::OwnedBox) || matches!( get_type_diagnostic_name(cx, ty), Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type | sym::RcWeak | sym::ArcWeak) From e1a4c90f61d830344b45d31f63ad544d6ea1d51a Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 06:09:11 -0400 Subject: [PATCH 52/89] Remove `get_type_diagnostic_name` --- clippy_lints/src/collection_is_never_read.rs | 3 +-- clippy_lints/src/doc/missing_headers.rs | 7 ++----- clippy_lints/src/eta_reduction.rs | 4 ++-- clippy_lints/src/infinite_iter.rs | 5 +++-- clippy_lints/src/manual_ignore_case_cmp.rs | 3 +-- clippy_lints/src/manual_retain.rs | 5 ++--- clippy_lints/src/matches/manual_unwrap_or.rs | 5 +++-- clippy_lints/src/methods/iter_nth.rs | 4 ++-- clippy_lints/src/methods/needless_collect.rs | 7 +++---- clippy_lints/src/methods/return_and_then.rs | 4 ++-- clippy_lints/src/methods/unnecessary_map_or.rs | 5 +++-- .../src/unnecessary_map_on_constructor.rs | 4 ++-- clippy_lints/src/unwrap.rs | 4 ++-- clippy_lints/src/useless_conversion.rs | 4 ++-- clippy_utils/src/ty/mod.rs | 18 ++---------------- 15 files changed, 32 insertions(+), 50 deletions(-) diff --git a/clippy_lints/src/collection_is_never_read.rs b/clippy_lints/src/collection_is_never_read.rs index e020f9e2ec5ba..95f3589c4477f 100644 --- a/clippy_lints/src/collection_is_never_read.rs +++ b/clippy_lints/src/collection_is_never_read.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::res::MaybeDef; -use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::visitors::{Visitable, for_each_expr}; use clippy_utils::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; @@ -60,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead { fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool { let ty = cx.typeck_results().pat_ty(local.pat); matches!( - get_type_diagnostic_name(cx, ty), + ty.opt_diag_name(cx), Some( sym::BTreeMap | sym::BTreeSet diff --git a/clippy_lints/src/doc/missing_headers.rs b/clippy_lints/src/doc/missing_headers.rs index 7c325132f8792..b164a9a99782b 100644 --- a/clippy_lints/src/doc/missing_headers.rs +++ b/clippy_lints/src/doc/missing_headers.rs @@ -2,7 +2,7 @@ use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_D use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::res::MaybeDef; -use clippy_utils::ty::{get_type_diagnostic_name, implements_trait_with_env}; +use clippy_utils::ty::implements_trait_with_env; use clippy_utils::visitors::for_each_expr; use clippy_utils::{fulfill_or_allowed, is_doc_hidden, is_inside_always_const_context, method_chain_args, return_ty}; use rustc_hir::{BodyId, FnSig, OwnerId, Safety}; @@ -120,10 +120,7 @@ fn find_panic(cx: &LateContext<'_>, body_id: BodyId) -> Option { if let Some(arglists) = method_chain_args(expr, &[sym::unwrap]).or_else(|| method_chain_args(expr, &[sym::expect])) && let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs() - && matches!( - get_type_diagnostic_name(cx, receiver_ty), - Some(sym::Option | sym::Result) - ) + && matches!(receiver_ty.opt_diag_name(cx), Some(sym::Option | sym::Result)) && !fulfill_or_allowed(cx, MISSING_PANICS_DOC, [expr.hir_id]) && panic_span.is_none() { diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 7c083eab88942..42b44778d58f5 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::VecArgs; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; -use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::usage::{local_used_after_expr, local_used_in}; use clippy_utils::{ get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id, @@ -144,7 +144,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx { let callee_ty_raw = typeck.expr_ty(callee); let callee_ty = callee_ty_raw.peel_refs(); - if matches!(get_type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc)) + if matches!(callee_ty.opt_diag_name(cx), Some(sym::Arc | sym::Rc)) || !check_inputs(typeck, body.params, None, args) { return; diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index bf3eafe09b3d0..f193f065e68d5 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::{get_type_diagnostic_name, implements_trait}; +use clippy_utils::res::MaybeDef; +use clippy_utils::ty::implements_trait; use clippy_utils::{higher, sym}; use rustc_hir::{BorrowKind, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -235,7 +236,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { } else if method.ident.name == sym::collect { let ty = cx.typeck_results().expr_ty(expr); if matches!( - get_type_diagnostic_name(cx, ty), + ty.opt_diag_name(cx), Some( sym::BinaryHeap | sym::BTreeMap diff --git a/clippy_lints/src/manual_ignore_case_cmp.rs b/clippy_lints/src/manual_ignore_case_cmp.rs index 18beff5b87091..25057b4aeaa27 100644 --- a/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/clippy_lints/src/manual_ignore_case_cmp.rs @@ -3,7 +3,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sym; -use clippy_utils::ty::get_type_diagnostic_name; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::ExprKind::{Binary, Lit, MethodCall}; @@ -59,7 +58,7 @@ fn get_ascii_type<'a>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'_>) -> Op if needs_ref_to_cmp(cx, ty) || ty.is_str() || ty.is_slice() - || matches!(get_type_diagnostic_name(cx, ty), Some(sym::OsStr | sym::OsString)) + || matches!(ty.opt_diag_name(cx), Some(sym::OsStr | sym::OsString)) { return Some((expr.span, ToAscii(is_lower, ty_raw))); } diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index 672a4c2676269..674f0da818f5f 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -4,7 +4,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use clippy_utils::ty::get_type_diagnostic_name; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::ExprKind::Assign; @@ -251,7 +250,7 @@ fn match_acceptable_sym(cx: &LateContext<'_>, collect_def_id: DefId) -> bool { fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Msrv) -> bool { let ty = cx.typeck_results().expr_ty(expr).peel_refs(); - let required = match get_type_diagnostic_name(cx, ty) { + let required = match ty.opt_diag_name(cx) { Some(sym::BinaryHeap) => msrvs::BINARY_HEAP_RETAIN, Some(sym::BTreeSet) => msrvs::BTREE_SET_RETAIN, Some(sym::BTreeMap) => msrvs::BTREE_MAP_RETAIN, @@ -265,7 +264,7 @@ fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Msrv) fn match_map_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr).peel_refs(); - matches!(get_type_diagnostic_name(cx, ty), Some(sym::BTreeMap | sym::HashMap)) + matches!(ty.opt_diag_name(cx), Some(sym::BTreeMap | sym::HashMap)) } fn make_span_lint_and_sugg(cx: &LateContext<'_>, span: Span, sugg: String) { diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index ac9e51890362b..db19090356e04 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,4 +1,5 @@ use clippy_utils::consts::ConstEvalCtxt; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt as _, indent_of, reindent_multiline}; use rustc_ast::{BindingMode, ByRef}; use rustc_errors::Applicability; @@ -10,7 +11,7 @@ use rustc_span::sym; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{expr_type_is_certain, get_type_diagnostic_name, implements_trait}; +use clippy_utils::ty::{expr_type_is_certain, implements_trait}; use clippy_utils::{is_default_equivalent, is_lint_allowed, path_res, peel_blocks, span_contains_comment}; use super::{MANUAL_UNWRAP_OR, MANUAL_UNWRAP_OR_DEFAULT}; @@ -174,7 +175,7 @@ fn handle( } fn find_type_name<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'static str> { - match get_type_diagnostic_name(cx, ty)? { + match ty.opt_diag_name(cx)? { sym::Option => Some("Option"), sym::Result => Some("Result"), _ => None, diff --git a/clippy_lints/src/methods/iter_nth.rs b/clippy_lints/src/methods/iter_nth.rs index 1fdbd81bf240e..6d1ee32e50269 100644 --- a/clippy_lints/src/methods/iter_nth.rs +++ b/clippy_lints/src/methods/iter_nth.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeDef; use clippy_utils::sym; -use clippy_utils::ty::get_type_diagnostic_name; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -16,7 +16,7 @@ pub(super) fn check<'tcx>( 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()) { + let caller_type = match cx.typeck_results().expr_ty(iter_recv).peel_refs().opt_diag_name(cx) { Some(sym::Vec) => "`Vec`", Some(sym::VecDeque) => "`VecDeque`", _ if cx.typeck_results().expr_ty_adjusted(iter_recv).peel_refs().is_slice() => "slice", diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 0075bf166cc18..34bae0132c97d 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -2,11 +2,10 @@ use std::ops::ControlFlow; use super::NEEDLESS_COLLECT; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{ - get_type_diagnostic_name, has_non_owning_mutable_access, make_normalized_projection, make_projection, -}; +use clippy_utils::ty::{has_non_owning_mutable_access, make_normalized_projection, make_projection}; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local, path_to_local_id, sym, @@ -98,7 +97,7 @@ pub(super) fn check<'tcx>( if let PatKind::Binding(BindingMode::NONE | BindingMode::MUT, id, _, None) = l.pat.kind && let ty = cx.typeck_results().expr_ty(collect_expr) && matches!( - get_type_diagnostic_name(cx, ty), + ty.opt_diag_name(cx), Some(sym::Vec | sym::VecDeque | sym::BinaryHeap | sym::LinkedList) ) && let iter_ty = cx.typeck_results().expr_ty(iter_expr) diff --git a/clippy_lints/src/methods/return_and_then.rs b/clippy_lints/src/methods/return_and_then.rs index 54f38a322b8d9..8f47306c89479 100644 --- a/clippy_lints/src/methods/return_and_then.rs +++ b/clippy_lints/src/methods/return_and_then.rs @@ -1,3 +1,4 @@ +use clippy_utils::res::MaybeDef; use rustc_errors::Applicability; use rustc_hir::{self as hir, Node}; use rustc_lint::LateContext; @@ -7,7 +8,6 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicability}; -use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::visitors::for_each_unconsumed_temporary; use clippy_utils::{peel_blocks, potential_return_of_enclosing_body}; @@ -26,7 +26,7 @@ pub(super) fn check<'tcx>( } let recv_type = cx.typeck_results().expr_ty(recv); - if !matches!(get_type_diagnostic_name(cx, recv_type), Some(sym::Option | sym::Result)) { + if !matches!(recv_type.opt_diag_name(cx), Some(sym::Option | sym::Result)) { return; } diff --git a/clippy_lints/src/methods/unnecessary_map_or.rs b/clippy_lints/src/methods/unnecessary_map_or.rs index 1f5e3de6e7a2d..bee8b6328a541 100644 --- a/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/clippy_lints/src/methods/unnecessary_map_or.rs @@ -3,8 +3,9 @@ use std::borrow::Cow; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; use clippy_utils::sugg::{Sugg, make_binop}; -use clippy_utils::ty::{get_type_diagnostic_name, implements_trait, is_copy}; +use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::visitors::is_local_used; use clippy_utils::{get_parent_expr, is_from_proc_macro, path_to_local_id}; use rustc_ast::LitKind::Bool; @@ -54,7 +55,7 @@ pub(super) fn check<'a>( return; }; - let variant = match get_type_diagnostic_name(cx, recv_ty) { + let variant = match recv_ty.opt_diag_name(cx) { Some(sym::Option) => Variant::Some, Some(sym::Result) => Variant::Ok, Some(_) | None => return, diff --git a/clippy_lints/src/unnecessary_map_on_constructor.rs b/clippy_lints/src/unnecessary_map_on_constructor.rs index d3700d05b014b..94b1a34455ff0 100644 --- a/clippy_lints/src/unnecessary_map_on_constructor.rs +++ b/clippy_lints/src/unnecessary_map_on_constructor.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::get_type_diagnostic_name; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -39,7 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor { return; } if let hir::ExprKind::MethodCall(path, recv, [map_arg], ..) = expr.kind - && let Some(sym::Option | sym::Result) = get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv)) + && let Some(sym::Option | sym::Result) = cx.typeck_results().expr_ty(recv).opt_diag_name(cx) { let (constructor_path, constructor_item) = if let hir::ExprKind::Call(constructor, [arg, ..]) = recv.kind && let hir::ExprKind::Path(constructor_path) = constructor.kind diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index aee8028a75de7..2f31aa55af6f8 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::Msrv; -use clippy_utils::ty::get_type_diagnostic_name; +use clippy_utils::res::MaybeDef; use clippy_utils::usage::is_potentially_local_place; use clippy_utils::{can_use_if_let_chains, higher, path_to_local, sym}; use rustc_errors::Applicability; @@ -147,7 +147,7 @@ fn collect_unwrap_info<'tcx>( is_entire_condition: bool, ) -> Vec> { fn option_or_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<(UnwrappableKind, bool)> { - match (get_type_diagnostic_name(cx, ty)?, method_name) { + match (ty.opt_diag_name(cx)?, method_name) { (sym::Option, sym::is_some) => Some((UnwrappableKind::Option, true)), (sym::Option, sym::is_none) => Some((UnwrappableKind::Option, false)), (sym::Result, sym::is_ok) => Some((UnwrappableKind::Result, true)), diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 2954bfea32a67..a99b5a9576a31 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lin use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; -use clippy_utils::ty::{get_type_diagnostic_name, is_copy, same_type_modulo_regions}; +use clippy_utils::ty::{is_copy, same_type_modulo_regions}; use clippy_utils::{ get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, sym, }; @@ -442,7 +442,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { fn has_eligible_receiver(cx: &LateContext<'_>, recv: &Expr<'_>, expr: &Expr<'_>) -> bool { if is_inherent_method_call(cx, expr) { matches!( - get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv)), + cx.typeck_results().expr_ty(recv).opt_diag_name(cx), Some(sym::Option | sym::Result | sym::ControlFlow) ) } else { diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index fb07f6c240381..03f9f42c09687 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -158,20 +158,6 @@ pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Optio .and_then(|iter_did| cx.get_associated_type(ty, iter_did, sym::Item)) } -/// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type -/// implements a trait marked with a diagnostic item use [`implements_trait`]. -/// -/// For a further exploitation what diagnostic items are see [diagnostic items] in -/// rustc-dev-guide. -/// -/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html -pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option { - match ty.kind() { - ty::Adt(adt, _) => cx.tcx.get_diagnostic_name(adt.did()), - _ => None, - } -} - /// Returns true if `ty` is a type on which calling `Clone` through a function instead of /// as a method, such as `Arc::clone()` is considered idiomatic. /// @@ -179,7 +165,7 @@ pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option, ty: Ty<'_>) -> bool { matches!( - get_type_diagnostic_name(cx, ty), + ty.opt_diag_name(cx), Some(sym::Arc | sym::ArcWeak | sym::Rc | sym::RcWeak) ) } @@ -400,7 +386,7 @@ pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { // Check for std types which implement drop, but only for memory allocation. else if ty.is_lang_item(cx, LangItem::OwnedBox) || matches!( - get_type_diagnostic_name(cx, ty), + ty.opt_diag_name(cx), Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type | sym::RcWeak | sym::ArcWeak) ) { From 53783de8f04132a3803f166aac02af55ad3254c7 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 06:30:02 -0400 Subject: [PATCH 53/89] Remove `MaybePath` --- clippy_lints/src/manual_clamp.rs | 6 +- clippy_lints/src/manual_let_else.rs | 60 +++++++------------ .../src/methods/unnecessary_literal_unwrap.rs | 9 ++- clippy_lints/src/utils/author.rs | 17 +++--- clippy_utils/src/lib.rs | 47 ++------------- clippy_utils/src/paths.rs | 5 +- 6 files changed, 48 insertions(+), 96 deletions(-) diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 42fe386d2c3c4..3d04c00051342 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -7,8 +7,8 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::is_const_evaluatable; use clippy_utils::{ - MaybePath, eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_res, path_to_local_id, - peel_blocks, peel_blocks_with_stmt, sym, + eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_res, path_to_local_id, peel_blocks, + peel_blocks_with_stmt, sym, }; use itertools::Itertools; use rustc_errors::{Applicability, Diag}; @@ -516,7 +516,7 @@ fn is_two_if_pattern<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> }, span: first_expr.span.to(second_expr.span), make_assignment: Some(maybe_input_first_path), - hir_with_ignore_attr: Some(first_expr.hir_id()), + hir_with_ignore_attr: Some(first_expr.hir_id), }) } else { None diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 3ed24b5dd2c55..298bf10754897 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -2,16 +2,14 @@ use crate::question_mark::{QUESTION_MARK, QuestionMark}; use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_context; -use clippy_utils::{ - MaybePath, is_lint_allowed, is_never_expr, is_wild, msrvs, pat_and_expr_can_be_question_mark, path_res, peel_blocks, -}; +use clippy_utils::{is_lint_allowed, is_never_expr, is_wild, msrvs, pat_and_expr_can_be_question_mark, peel_blocks}; use rustc_ast::BindingMode; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::{CtorOf, DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::{Arm, Expr, ExprKind, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_span::Span; use rustc_span::symbol::{Symbol, sym}; @@ -131,39 +129,25 @@ fn is_arms_disjointed(cx: &LateContext<'_>, arm1: &Arm<'_>, arm2: &Arm<'_>) -> b /// Returns `true` if the given pattern is a variant of an enum. pub fn is_enum_variant(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { - struct Pat<'hir>(&'hir rustc_hir::Pat<'hir>); - - impl<'hir> MaybePath<'hir> for Pat<'hir> { - fn qpath_opt(&self) -> Option<&QPath<'hir>> { - match self.0.kind { - PatKind::Struct(ref qpath, fields, _) - if fields - .iter() - .all(|field| is_wild(field.pat) || matches!(field.pat.kind, PatKind::Binding(..))) => - { - Some(qpath) - }, - PatKind::TupleStruct(ref qpath, pats, _) - if pats - .iter() - .all(|pat| is_wild(pat) || matches!(pat.kind, PatKind::Binding(..))) => - { - Some(qpath) - }, - PatKind::Expr(&PatExpr { - kind: PatExprKind::Path(ref qpath), - .. - }) => Some(qpath), - _ => None, - } - } - - fn hir_id(&self) -> HirId { - self.0.hir_id - } - } - - let res = path_res(cx, &Pat(pat)); + let path = match pat.kind { + PatKind::Struct(ref qpath, fields, _) + if fields + .iter() + .all(|field| is_wild(field.pat) || matches!(field.pat.kind, PatKind::Binding(..))) => + { + (qpath, pat.hir_id) + }, + PatKind::TupleStruct(ref qpath, pats, _) + if pats + .iter() + .all(|pat| is_wild(pat) || matches!(pat.kind, PatKind::Binding(..))) => + { + (qpath, pat.hir_id) + }, + PatKind::Expr(e) if let Some((qpath, id)) = e.opt_qpath() => (qpath, id), + _ => return false, + }; + let res = path.res(cx); matches!( res, Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) diff --git a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index cc4448192d3e7..f85d47d4d1831 100644 --- a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{MaybePath, is_res_lang_ctor, last_path_segment, path_res, sym}; +use clippy_utils::res::MaybeQPath; +use clippy_utils::{is_res_lang_ctor, last_path_segment, path_res, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, AmbigArg}; use rustc_lint::LateContext; @@ -37,10 +38,12 @@ pub(super) fn check( } let (constructor, call_args, ty) = if let hir::ExprKind::Call(call, call_args) = init.kind { - let Some(qpath) = call.qpath_opt() else { return }; + let Some((qpath, hir_id)) = call.opt_qpath() else { + return; + }; let args = last_path_segment(qpath).args.map(|args| args.args); - let res = cx.qpath_res(qpath, call.hir_id()); + let res = cx.qpath_res(qpath, hir_id); if is_res_lang_ctor(cx, res, hir::LangItem::OptionSome) { (sym::Some, call_args, get_ty_from_args(args, 0)) diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 0c388fbd4812f..bfd115ec18d79 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -1,4 +1,5 @@ -use clippy_utils::{MaybePath, get_attr, higher, path_def_id, sym}; +use clippy_utils::res::MaybeQPath; +use clippy_utils::{get_attr, higher, path_def_id, sym}; use itertools::Itertools; use rustc_ast::LitIntType; use rustc_ast::ast::{LitFloatType, LitKind}; @@ -268,16 +269,16 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { chain!(self, "{symbol}.as_str() == {:?}", symbol.value.as_str()); } - fn qpath<'p>(&self, qpath: &Binding<&QPath<'_>>, has_hir_id: &Binding<&impl MaybePath<'p>>) { + fn qpath(&self, qpath: &Binding<&QPath<'_>>, hir_id_binding: &str, hir_id: HirId) { if let QPath::LangItem(lang_item, ..) = *qpath.value { chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))"); - } else if let Some(def_id) = self.cx.qpath_res(qpath.value, has_hir_id.value.hir_id()).opt_def_id() + } else if let Some(def_id) = self.cx.qpath_res(qpath.value, hir_id).opt_def_id() && !def_id.is_local() { bind!(self, def_id); chain!( self, - "let Some({def_id}) = cx.qpath_res({qpath}, {has_hir_id}.hir_id).opt_def_id()" + "let Some({def_id}) = cx.qpath_res({qpath}, {hir_id_binding}.hir_id).opt_def_id()" ); if let Some(name) = self.cx.tcx.get_diagnostic_name(def_id.value) { chain!(self, "cx.tcx.is_diagnostic_item(sym::{name}, {def_id})"); @@ -291,7 +292,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } } - fn maybe_path<'p>(&self, path: &Binding<&impl MaybePath<'p>>) { + fn maybe_path<'p>(&self, path: &Binding>) { if let Some(id) = path_def_id(self.cx, path.value) && !id.is_local() { @@ -671,7 +672,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { StructTailExpr::None | StructTailExpr::DefaultFields(_) => None, }); kind!("Struct({qpath}, {fields}, {base})"); - self.qpath(qpath, expr); + self.qpath(qpath, &expr.name, expr.value.hir_id); self.slice(fields, |field| { self.ident(field!(field.ident)); self.expr(field!(field.expr)); @@ -757,7 +758,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { let ignore = etc.is_some(); bind!(self, qpath, fields); kind!("Struct(ref {qpath}, {fields}, {ignore})"); - self.qpath(qpath, pat); + self.qpath(qpath, &pat.name, pat.value.hir_id); self.slice(fields, |field| { self.ident(field!(field.ident)); self.pat(field!(field.pat)); @@ -771,7 +772,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { PatKind::TupleStruct(ref qpath, fields, skip_pos) => { bind!(self, qpath, fields); kind!("TupleStruct(ref {qpath}, {fields}, {skip_pos:?})"); - self.qpath(qpath, pat); + self.qpath(qpath, &pat.name, pat.value.hir_id); self.slice(fields, |pat| self.pat(pat)); }, PatKind::Tuple(fields, skip_pos) => { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index a976c9a7dafe1..59b6b3271c44f 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -132,7 +132,7 @@ use crate::ast_utils::unordered_over; use crate::consts::{ConstEvalCtxt, Constant}; use crate::higher::Range; use crate::msrvs::Msrv; -use crate::res::{MaybeDef, MaybeResPath}; +use crate::res::{MaybeDef, MaybeQPath, MaybeResPath}; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -471,53 +471,16 @@ pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option { } } -pub trait MaybePath<'hir> { - fn hir_id(&self) -> HirId; - fn qpath_opt(&self) -> Option<&QPath<'hir>>; -} - -macro_rules! maybe_path { - ($ty:ident, $kind:ident) => { - impl<'hir> MaybePath<'hir> for hir::$ty<'hir> { - fn hir_id(&self) -> HirId { - self.hir_id - } - fn qpath_opt(&self) -> Option<&QPath<'hir>> { - match &self.kind { - hir::$kind::Path(qpath) => Some(qpath), - _ => None, - } - } - } - }; -} -maybe_path!(Expr, ExprKind); -impl<'hir> MaybePath<'hir> for Pat<'hir> { - fn hir_id(&self) -> HirId { - self.hir_id - } - fn qpath_opt(&self) -> Option<&QPath<'hir>> { - match &self.kind { - PatKind::Expr(PatExpr { - kind: PatExprKind::Path(qpath), - .. - }) => Some(qpath), - _ => None, - } - } -} -maybe_path!(Ty, TyKind); - /// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err` -pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res { - match maybe_path.qpath_opt() { +pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: impl MaybeQPath<'tcx>) -> Res { + match maybe_path.opt_qpath() { None => Res::Err, - Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()), + Some((qpath, id)) => cx.qpath_res(qpath, id), } } /// If `maybe_path` is a path node which resolves to an item, retrieves the item ID -pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option { +pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: impl MaybeQPath<'tcx>) -> Option { path_res(cx, maybe_path).opt_def_id() } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 5ab8e16d88ed3..ff7c26d851bbe 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -4,7 +4,8 @@ //! Whenever possible, please consider diagnostic items over hardcoded paths. //! See for more information. -use crate::{MaybePath, path_def_id, sym}; +use crate::res::MaybeQPath; +use crate::{path_def_id, sym}; use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS}; @@ -96,7 +97,7 @@ impl PathLookup { } /// Resolves `maybe_path` to a [`DefId`] and checks if the [`PathLookup`] matches it - pub fn matches_path<'tcx>(&self, cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> bool { + pub fn matches_path<'tcx>(&self, cx: &LateContext<'_>, maybe_path: impl MaybeQPath<'tcx>) -> bool { path_def_id(cx, maybe_path).is_some_and(|def_id| self.matches(cx, def_id)) } From 5b659ba0b4d076255af21a0ec90d43ed2b91dd03 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 06:28:29 -0400 Subject: [PATCH 54/89] Remove `path_res` --- .../src/assertions_on_result_states.rs | 6 ++-- clippy_lints/src/derive/mod.rs | 4 +-- clippy_lints/src/error_impl_error.rs | 4 +-- clippy_lints/src/if_then_some_else_none.rs | 7 ++-- clippy_lints/src/loops/manual_find.rs | 10 +++--- clippy_lints/src/manual_clamp.rs | 5 +-- clippy_lints/src/matches/manual_filter.rs | 8 ++--- clippy_lints/src/matches/manual_map.rs | 5 +-- clippy_lints/src/matches/manual_ok_err.rs | 7 ++-- clippy_lints/src/matches/manual_unwrap_or.rs | 7 ++-- clippy_lints/src/matches/manual_utils.rs | 6 ++-- clippy_lints/src/matches/match_as_ref.rs | 5 +-- clippy_lints/src/matches/needless_match.rs | 6 ++-- clippy_lints/src/matches/try_err.rs | 5 +-- clippy_lints/src/mem_replace.rs | 9 +++--- .../iter_on_single_or_empty_collections.rs | 5 +-- clippy_lints/src/methods/manual_ok_or.rs | 8 ++--- .../methods/manual_saturating_arithmetic.rs | 5 +-- .../src/methods/needless_option_as_deref.rs | 6 ++-- .../src/methods/option_map_or_none.rs | 8 ++--- clippy_lints/src/methods/or_then_unwrap.rs | 6 ++-- .../src/methods/result_map_or_else_none.rs | 8 ++--- .../src/methods/unnecessary_filter_map.rs | 7 ++-- .../src/methods/unnecessary_literal_unwrap.rs | 4 +-- clippy_lints/src/needless_question_mark.rs | 4 +-- clippy_lints/src/non_canonical_impls.rs | 32 ++++++++----------- clippy_lints/src/partialeq_to_none.rs | 6 ++-- clippy_lints/src/question_mark.rs | 8 ++--- .../needless_return_with_question_mark.rs | 5 +-- clippy_lints/src/single_option_map.rs | 9 +++--- clippy_lints/src/unnecessary_literal_bound.rs | 4 +-- clippy_lints/src/unnecessary_wraps.rs | 5 +-- clippy_utils/src/lib.rs | 12 ++----- clippy_utils/src/ty/mod.rs | 5 ++- 34 files changed, 119 insertions(+), 122 deletions(-) diff --git a/clippy_lints/src/assertions_on_result_states.rs b/clippy_lints/src/assertions_on_result_states.rs index 191fbf65e5a6c..cc62306b33b53 100644 --- a/clippy_lints/src/assertions_on_result_states.rs +++ b/clippy_lints/src/assertions_on_result_states.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node}; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_context; +use clippy_utils::sym; use clippy_utils::ty::{has_debug_impl, is_copy}; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::{path_res, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Expr, ExprKind, Node}; @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { if !is_copy(cx, result_type) { if result_type_with_refs != result_type { return; - } else if let Res::Local(binding_id) = path_res(cx, recv) + } else if let Res::Local(binding_id) = *recv.basic_res() && local_used_after_expr(cx, binding_id, recv) { return; diff --git a/clippy_lints/src/derive/mod.rs b/clippy_lints/src/derive/mod.rs index 06efc2709faa6..eafe7c4bb9f23 100644 --- a/clippy_lints/src/derive/mod.rs +++ b/clippy_lints/src/derive/mod.rs @@ -1,4 +1,4 @@ -use clippy_utils::path_res; +use clippy_utils::res::MaybeResPath; use rustc_hir::def::Res; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -199,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { self_ty, .. }) = item.kind - && let Res::Def(_, def_id) = path_res(cx, self_ty) + && let Res::Def(_, def_id) = *self_ty.basic_res() && let Some(local_def_id) = def_id.as_local() { let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); diff --git a/clippy_lints/src/error_impl_error.rs b/clippy_lints/src/error_impl_error.rs index 3018e1f127347..3d8650f05168d 100644 --- a/clippy_lints/src/error_impl_error.rs +++ b/clippy_lints/src/error_impl_error.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_hir_and_then}; -use clippy_utils::path_res; +use clippy_utils::res::MaybeResPath; use clippy_utils::ty::implements_trait; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{Item, ItemKind}; @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError { if let Some(trait_def_id) = imp.of_trait.and_then(|t| t.trait_ref.trait_def_id()) && let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error) && error_def_id == trait_def_id - && let Some(def_id) = path_res(cx, imp.self_ty).opt_def_id().and_then(DefId::as_local) + && let Some(def_id) = imp.self_ty.basic_res().opt_def_id().and_then(DefId::as_local) && let Some(ident) = cx.tcx.opt_item_ident(def_id.to_def_id()) && ident.name == sym::Error && is_visible_outside_module(cx, def_id) => diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index f9fee292837ea..11338e1536242 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -2,11 +2,12 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}; use clippy_utils::sugg::Sugg; use clippy_utils::{ contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, - path_res, peel_blocks, sym, + peel_blocks, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -73,8 +74,8 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind && !expr.span.from_expansion() && !then_expr.span.from_expansion() - && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) - && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) + && is_res_lang_ctor(cx, then_call.res(cx), OptionSome) + && is_res_lang_ctor(cx, peel_blocks(els).res(cx), OptionNone) && !is_else_clause(cx.tcx, expr) && !is_in_const_context(cx) && self.msrv.meets(cx, msrvs::BOOL_THEN) diff --git a/clippy_lints/src/loops/manual_find.rs b/clippy_lints/src/loops/manual_find.rs index f99989ec6ba4b..5fb40413955cb 100644 --- a/clippy_lints/src/loops/manual_find.rs +++ b/clippy_lints/src/loops/manual_find.rs @@ -1,12 +1,12 @@ use super::MANUAL_FIND; use super::utils::make_iterator_snippet; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeQPath, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt}; +use clippy_utils::{higher, is_res_lang_ctor, peel_blocks_with_stmt}; use rustc_errors::Applicability; -use rustc_hir::def::Res; use rustc_hir::lang_items::LangItem; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind}; use rustc_lint::LateContext; @@ -34,8 +34,8 @@ pub(super) fn check<'tcx>( && let StmtKind::Semi(semi) = stmt.kind && let ExprKind::Ret(Some(ret_value)) = semi.kind && let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind - && is_res_lang_ctor(cx, path_res(cx, ctor), LangItem::OptionSome) - && path_res(cx, inner_ret) == Res::Local(binding_id) + && is_res_lang_ctor(cx, ctor.res(cx), LangItem::OptionSome) + && inner_ret.res_local_id() == Some(binding_id) && !contains_return_break_continue_macro(cond) && let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr) { @@ -150,7 +150,7 @@ fn last_stmt_and_ret<'tcx>( && let Some((_, Node::Block(block))) = parent_iter.next() && let Some((last_stmt, last_ret)) = extract(block) && last_stmt.hir_id == node_hir - && is_res_lang_ctor(cx, path_res(cx, last_ret), LangItem::OptionNone) + && is_res_lang_ctor(cx, last_ret.res(cx), LangItem::OptionNone) && let Some((_, Node::Expr(_block))) = parent_iter.next() // This includes the function header && let Some((_, func)) = parent_iter.next() diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 3d04c00051342..eaaab292f2f53 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -3,11 +3,12 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::higher::If; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::is_const_evaluatable; use clippy_utils::{ - eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_res, path_to_local_id, peel_blocks, + eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_to_local_id, peel_blocks, peel_blocks_with_stmt, sym, }; use itertools::Itertools; @@ -342,7 +343,7 @@ fn is_call_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) } }, ExprKind::Path(QPath::TypeRelative(ty, seg)) => { - matches!(path_res(cx, ty), Res::PrimTy(PrimTy::Float(_))).then(|| FunctionType::OrdOrFloat(seg)) + matches!(ty.basic_res(), Res::PrimTy(PrimTy::Float(_))).then(|| FunctionType::OrdOrFloat(seg)) }, _ => None, } diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index 96252a82fc0d9..138d733e46d22 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::visitors::contains_unsafe_block; -use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; +use clippy_utils::{is_res_lang_ctor, path_to_local_id}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind}; @@ -66,7 +66,7 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: && let ExprKind::Call(callee, [arg]) = inner_expr.kind { return ctxt == expr.span.ctxt() - && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) + && is_res_lang_ctor(cx, callee.res(cx), OptionSome) && path_to_local_id(arg, target); } false @@ -74,7 +74,7 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { - return is_res_lang_ctor(cx, path_res(cx, inner_expr), OptionNone); + return is_res_lang_ctor(cx, inner_expr.res(cx), OptionNone); } false } diff --git a/clippy_lints/src/matches/manual_map.rs b/clippy_lints/src/matches/manual_map.rs index de57d1eee9249..602a3ab1bb9d0 100644 --- a/clippy_lints/src/matches/manual_map.rs +++ b/clippy_lints/src/matches/manual_map.rs @@ -2,8 +2,9 @@ use super::MANUAL_MAP; use super::manual_utils::{SomeExpr, check_with}; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_res_lang_ctor, path_res}; +use clippy_utils::is_res_lang_ctor; +use clippy_utils::res::MaybeQPath; use rustc_hir::LangItem::OptionSome; use rustc_hir::{Arm, Block, BlockCheckMode, Expr, ExprKind, Pat, UnsafeSource}; use rustc_lint::LateContext; @@ -91,7 +92,7 @@ fn get_some_expr<'tcx>( // TODO: Allow more complex expressions. match expr.kind { ExprKind::Call(callee, [arg]) - if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) => + if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, callee.res(cx), OptionSome) => { Some(SomeExpr::new_no_negated(arg, needs_unsafe_block)) }, diff --git a/clippy_lints/src/matches/manual_ok_err.rs b/clippy_lints/src/matches/manual_ok_err.rs index a8490d6aa7d82..fa2ab773c5393 100644 --- a/clippy_lints/src/matches/manual_ok_err.rs +++ b/clippy_lints/src/matches/manual_ok_err.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::{indent_of, reindent_multiline}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{option_arg_ty, peel_and_count_ty_refs}; -use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res, peel_blocks, span_contains_comment}; +use clippy_utils::{get_parent_expr, is_res_lang_ctor, peel_blocks, span_contains_comment}; use rustc_ast::{BindingMode, Mutability}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr}; @@ -103,7 +104,7 @@ fn is_ok_or_err<'hir>(cx: &LateContext<'_>, pat: &Pat<'hir>) -> Option<(bool, &' /// Check if `expr` contains `Some(ident)`, possibly as a block fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, ty: Ty<'tcx>) -> bool { if let ExprKind::Call(body_callee, [body_arg]) = peel_blocks(expr).kind - && is_res_lang_ctor(cx, path_res(cx, body_callee), OptionSome) + && is_res_lang_ctor(cx, body_callee.res(cx), OptionSome) && cx.typeck_results().expr_ty(body_arg) == ty && let ExprKind::Path(QPath::Resolved( _, @@ -120,7 +121,7 @@ fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, t /// Check if `expr` is `None`, possibly as a block fn is_none(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) + is_res_lang_ctor(cx, peel_blocks(expr).res(cx), OptionNone) } /// Suggest replacing `expr` by `scrutinee.METHOD()`, where `METHOD` is either `ok` or diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index db19090356e04..df886efc66840 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,5 +1,5 @@ use clippy_utils::consts::ConstEvalCtxt; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{SpanRangeExt as _, indent_of, reindent_multiline}; use rustc_ast::{BindingMode, ByRef}; use rustc_errors::Applicability; @@ -12,7 +12,7 @@ use rustc_span::sym; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{expr_type_is_certain, implements_trait}; -use clippy_utils::{is_default_equivalent, is_lint_allowed, path_res, peel_blocks, span_contains_comment}; +use clippy_utils::{is_default_equivalent, is_lint_allowed, peel_blocks, span_contains_comment}; use super::{MANUAL_UNWRAP_OR, MANUAL_UNWRAP_OR_DEFAULT}; @@ -115,7 +115,8 @@ fn handle( && is_default_equivalent(cx, peel_blocks(body_none)) { // We now check if the condition is a None variant, in which case we need to specify the type - if path_res(cx, condition) + if condition + .res(cx) .opt_def_id() .is_some_and(|id| Some(cx.tcx.parent(id)) == cx.tcx.lang_items().option_none_variant()) { diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index 5e989beb9fd1f..9477e98acde5c 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -1,12 +1,12 @@ use crate::map_unit_fn::OPTION_MAP_UNIT_FN; use crate::matches::MATCH_AS_REF; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_copy, is_unsafe_fn, peel_and_count_ty_refs}; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, is_res_lang_ctor, - path_res, path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, + path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; @@ -273,5 +273,5 @@ pub(super) fn try_parse_pattern<'tcx>( // Checks for the `None` value. fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) + is_res_lang_ctor(cx, peel_blocks(expr).res(cx), OptionNone) } diff --git a/clippy_lints/src/matches/match_as_ref.rs b/clippy_lints/src/matches/match_as_ref.rs index 1cb4b512a30e5..3e236e82635f6 100644 --- a/clippy_lints/src/matches/match_as_ref.rs +++ b/clippy_lints/src/matches/match_as_ref.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_none_arm, is_res_lang_ctor, path_res, peel_blocks}; +use clippy_utils::{is_none_arm, is_res_lang_ctor, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath}; use rustc_lint::LateContext; @@ -61,7 +62,7 @@ fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionSome) && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., ident, _) = first_pat.kind && let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind - && is_res_lang_ctor(cx, path_res(cx, e), LangItem::OptionSome) + && is_res_lang_ctor(cx, e.res(cx), LangItem::OptionSome) && let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind && path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index e6f47707e3a29..a13f9f35eea3e 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -1,10 +1,10 @@ use super::NEEDLESS_MATCH; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::same_type_modulo_regions; use clippy_utils::{ - SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, is_res_lang_ctor, over, path_res, + SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, is_res_lang_ctor, over, peel_blocks_with_stmt, }; use rustc_errors::Applicability; @@ -106,7 +106,7 @@ fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool } let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr); if let_expr_ty.is_diag_item(cx, sym::Option) { - return is_res_lang_ctor(cx, path_res(cx, else_expr), OptionNone) + return is_res_lang_ctor(cx, else_expr.res(cx), OptionNone) || eq_expr_value(cx, if_let.let_expr, else_expr); } return eq_expr_value(cx, if_let.let_expr, else_expr); diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs index af90cb5e67334..4fd4851eab60a 100644 --- a/clippy_lints/src/matches/try_err.rs +++ b/clippy_lints/src/matches/try_err.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::option_arg_ty; -use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res}; +use clippy_utils::{get_parent_expr, is_res_lang_ctor}; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -25,7 +26,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine && let ExprKind::Path(ref match_fun_path) = match_fun.kind && matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)) && let ExprKind::Call(err_fun, [err_arg]) = try_arg.kind - && is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr) + && is_res_lang_ctor(cx, err_fun.res(cx), ResultErr) && let Some(return_ty) = find_return_type(cx, &expr.kind) { let (prefix, suffix, err_ty) = if let Some(ty) = result_error_type(cx, return_ty) { diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 95ecef5987007..7d042cd490fde 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,12 +1,11 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_non_aggregate_primitive_type; -use clippy_utils::{ - is_default_equivalent, is_expr_used_or_unified, is_res_lang_ctor, path_res, peel_ref_operators, std_or_core, -}; +use clippy_utils::{is_default_equivalent, is_expr_used_or_unified, is_res_lang_ctor, peel_ref_operators, std_or_core}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; @@ -129,7 +128,7 @@ impl_lint_pass!(MemReplace => [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_OPTION_WITH_SOME, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]); fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) -> bool { - if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) { + if is_res_lang_ctor(cx, src.res(cx), OptionNone) { // Since this is a late pass (already type-checked), // and we already know that the second argument is an // `Option`, we do not need to check the first @@ -163,7 +162,7 @@ fn check_replace_option_with_some( msrv: Msrv, ) -> bool { if let ExprKind::Call(src_func, [src_arg]) = src.kind - && is_res_lang_ctor(cx, path_res(cx, src_func), OptionSome) + && is_res_lang_ctor(cx, src_func.res(cx), OptionSome) && msrv.meets(cx, msrvs::OPTION_REPLACE) { // We do not have to check for a `const` context here, because `core::mem::replace()` and diff --git a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 83e565562af08..728240c724b48 100644 --- a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -1,9 +1,10 @@ use std::iter::once; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::snippet; use clippy_utils::ty::{ExprFnSig, expr_sig, ty_sig}; -use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core, sym}; +use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, std_or_core, sym}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -68,7 +69,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method ExprKind::Array([]) => None, ExprKind::Array([e]) => Some(e), ExprKind::Path(ref p) if is_res_lang_ctor(cx, cx.qpath_res(p, recv.hir_id), OptionNone) => None, - ExprKind::Call(f, [arg]) if is_res_lang_ctor(cx, path_res(cx, f), OptionSome) => Some(arg), + ExprKind::Call(f, [arg]) if is_res_lang_ctor(cx, f.res(cx), OptionSome) => Some(arg), _ => return, }; let iter_type = match method_name { diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs index 610368aabc5b5..d84f188613fb3 100644 --- a/clippy_lints/src/methods/manual_ok_or.rs +++ b/clippy_lints/src/methods/manual_ok_or.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; -use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; +use clippy_utils::{is_res_lang_ctor, path_to_local_id}; use rustc_errors::Applicability; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{Expr, ExprKind, PatKind}; @@ -25,7 +25,7 @@ pub(super) fn check<'tcx>( .instantiate_identity() .is_diag_item(cx, sym::Option) && let ExprKind::Call(err_path, [err_arg]) = or_expr.kind - && is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr) + && is_res_lang_ctor(cx, err_path.res(cx), ResultErr) && is_ok_wrapping(cx, map_expr) && let Some(recv_snippet) = recv.span.get_source_text(cx) && let Some(err_arg_snippet) = err_arg.span.get_source_text(cx) @@ -51,7 +51,7 @@ fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { let body = cx.tcx.hir_body(closure.body); if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind && let ExprKind::Call(callee, [ok_arg]) = body.value.kind - && is_res_lang_ctor(cx, path_res(cx, callee), ResultOk) + && is_res_lang_ctor(cx, callee.res(cx), ResultOk) { path_to_local_id(ok_arg, param_id) } else { diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index c785b23bc9c4a..2196ce92b0ab5 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{path_res, sym}; +use clippy_utils::sym; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; @@ -83,7 +84,7 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { // `T::MAX` and `T::MIN` constants if let hir::ExprKind::Path(hir::QPath::TypeRelative(base, seg)) = expr.kind - && let Res::PrimTy(_) = path_res(cx, base) + && matches!(base.basic_res(), Res::PrimTy(_)) { match seg.ident.name { sym::MAX => return Some(MinMax::Max), diff --git a/clippy_lints/src/methods/needless_option_as_deref.rs b/clippy_lints/src/methods/needless_option_as_deref.rs index b4361448404a1..06e6a3c70b87d 100644 --- a/clippy_lints/src/methods/needless_option_as_deref.rs +++ b/clippy_lints/src/methods/needless_option_as_deref.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::SpanRangeExt; +use clippy_utils::sym; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::{path_res, sym}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_hir::def::Res; @@ -17,7 +17,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name if outer_ty.is_diag_item(cx, sym::Option) && outer_ty == typeck.expr_ty(recv) { if name == sym::as_deref_mut && recv.is_syntactic_place_expr() { - let Res::Local(binding_id) = path_res(cx, recv) else { + let Res::Local(binding_id) = *recv.basic_res() else { return; }; diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 2b94ecd470646..e52a8a0266d50 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; -use clippy_utils::{is_res_lang_ctor, path_def_id, path_res}; +use clippy_utils::{is_res_lang_ctor, path_def_id}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -49,12 +49,12 @@ pub(super) fn check<'tcx>( return; } - if !is_res_lang_ctor(cx, path_res(cx, def_arg), OptionNone) { + if !is_res_lang_ctor(cx, def_arg.res(cx), OptionNone) { // nothing to lint! return; } - let f_arg_is_some = is_res_lang_ctor(cx, path_res(cx, map_arg), OptionSome); + let f_arg_is_some = is_res_lang_ctor(cx, map_arg.res(cx), OptionSome); if is_option { let self_snippet = snippet(cx, recv.span, ".."); diff --git a/clippy_lints/src/methods/or_then_unwrap.rs b/clippy_lints/src/methods/or_then_unwrap.rs index f99e68fc419de..84b886d4fbe5a 100644 --- a/clippy_lints/src/methods/or_then_unwrap.rs +++ b/clippy_lints/src/methods/or_then_unwrap.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::is_res_lang_ctor; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_res_lang_ctor, path_res}; use rustc_errors::Applicability; use rustc_hir::lang_items::LangItem; use rustc_hir::{Expr, ExprKind}; @@ -60,7 +60,7 @@ pub(super) fn check<'tcx>( fn get_content_if_ctor_matches(cx: &LateContext<'_>, expr: &Expr<'_>, item: LangItem) -> Option { if let ExprKind::Call(some_expr, [arg]) = expr.kind - && is_res_lang_ctor(cx, path_res(cx, some_expr), item) + && is_res_lang_ctor(cx, some_expr.res(cx), item) { Some(arg.span.source_callsite()) } else { diff --git a/clippy_lints/src/methods/result_map_or_else_none.rs b/clippy_lints/src/methods/result_map_or_else_none.rs index 7aeda9493c2c0..de0bd78c9354e 100644 --- a/clippy_lints/src/methods/result_map_or_else_none.rs +++ b/clippy_lints/src/methods/result_map_or_else_none.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; -use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks}; +use clippy_utils::{is_res_lang_ctor, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -21,11 +21,11 @@ pub(super) fn check<'tcx>( // lint if the caller of `map_or_else()` is a `Result` if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) // We check that it is mapped as `Some`. - && is_res_lang_ctor(cx, path_res(cx, map_arg), OptionSome) + && is_res_lang_ctor(cx, map_arg.res(cx), OptionSome) && let hir::ExprKind::Closure(&hir::Closure { body, .. }) = def_arg.kind && let body = cx.tcx.hir_body(body) // And finally we check that we return a `None` in the "else case". - && is_res_lang_ctor(cx, path_res(cx, peel_blocks(body.value)), OptionNone) + && is_res_lang_ctor(cx, peel_blocks(body.value).res(cx), OptionNone) { let msg = "called `map_or_else(|_| None, Some)` on a `Result` value"; let self_snippet = snippet(cx, recv.span, ".."); diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index d260e0ef6e197..ac6a2e630cf31 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,9 +1,10 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint; +use clippy_utils::res::MaybeQPath; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; -use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id, sym}; +use clippy_utils::{is_res_lang_ctor, is_trait_method, path_to_local_id, sym}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -46,7 +47,7 @@ pub(super) fn check<'tcx>( // Check if the closure is .filter_map(|x| Some(x)) if name == sym::filter_map && let hir::ExprKind::Call(expr, args) = body.value.kind - && is_res_lang_ctor(cx, path_res(cx, expr), OptionSome) + && is_res_lang_ctor(cx, expr.res(cx), OptionSome) && let hir::ExprKind::Path(_) = args[0].kind { span_lint( @@ -95,7 +96,7 @@ pub(super) fn check<'tcx>( fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) { match expr.kind { hir::ExprKind::Call(func, args) => { - if is_res_lang_ctor(cx, path_res(cx, func), OptionSome) { + if is_res_lang_ctor(cx, func.res(cx), OptionSome) { if path_to_local_id(&args[0], arg_id) { return (false, false); } diff --git a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index f85d47d4d1831..dc5ce293aa8f3 100644 --- a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeQPath; -use clippy_utils::{is_res_lang_ctor, last_path_segment, path_res, sym}; +use clippy_utils::{is_res_lang_ctor, last_path_segment, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, AmbigArg}; use rustc_lint::LateContext; @@ -54,7 +54,7 @@ pub(super) fn check( } else { return; } - } else if is_res_lang_ctor(cx, path_res(cx, init), hir::LangItem::OptionNone) { + } else if is_res_lang_ctor(cx, init.res(cx), hir::LangItem::OptionNone) { let call_args: &[hir::Expr<'_>] = &[]; (sym::None, call_args, None) } else { diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 2a2160c3be2d1..986a827c59c55 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::path_res; +use clippy_utils::res::MaybeQPath; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Block, Body, Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -94,7 +94,7 @@ impl LateLintPass<'_> for NeedlessQuestionMark { fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Call(path, [arg]) = expr.kind - && let Res::Def(DefKind::Ctor(..), ctor_id) = path_res(cx, path) + && let Res::Def(DefKind::Ctor(..), ctor_id) = path.res(cx) && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) && let variant = if cx.tcx.lang_items().option_some_variant() == Some(variant_id) { "Some" diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index e531f797272d2..0bb6a585789e2 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -1,13 +1,11 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::MaybeQPath; use clippy_utils::ty::implements_trait; -use clippy_utils::{ - is_diag_trait_item, is_from_proc_macro, is_res_lang_ctor, last_path_segment, path_res, std_or_core, -}; +use clippy_utils::{is_diag_trait_item, is_from_proc_macro, is_res_lang_ctor, last_path_segment, std_or_core}; use rustc_errors::Applicability; -use rustc_hir::def_id::LocalDefId; use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, LangItem, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{EarlyBinder, TraitRef}; +use rustc_middle::ty::{EarlyBinder, TraitRef, TypeckResults}; use rustc_session::declare_lint_pass; use rustc_span::sym; use rustc_span::symbol::kw; @@ -261,7 +259,7 @@ fn expr_is_cmp<'tcx>( impl_item: &ImplItem<'_>, needs_fully_qualified: &mut bool, ) -> bool { - let impl_item_did = impl_item.owner_id.def_id; + let typeck = cx.tcx.typeck(impl_item.owner_id.def_id); match expr.kind { ExprKind::Call( Expr { @@ -271,16 +269,15 @@ fn expr_is_cmp<'tcx>( }, [cmp_expr], ) => { - is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) - // Fix #11178, allow `Self::cmp(self, ..)` too - && self_cmp_call(cx, cmp_expr, impl_item_did, needs_fully_qualified) + is_res_lang_ctor(cx, typeck.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) + // Fix #11178, allow `Self::cmp(self, ..)` + && self_cmp_call(cx, typeck, cmp_expr, needs_fully_qualified) }, ExprKind::MethodCall(_, recv, [], _) => { - cx.tcx - .typeck(impl_item_did) + typeck .type_dependent_def_id(expr.hir_id) .is_some_and(|def_id| is_diag_trait_item(cx, def_id, sym::Into)) - && self_cmp_call(cx, recv, impl_item_did, needs_fully_qualified) + && self_cmp_call(cx, typeck, recv, needs_fully_qualified) }, _ => false, } @@ -289,12 +286,13 @@ fn expr_is_cmp<'tcx>( /// Returns whether this is any of `self.cmp(..)`, `Self::cmp(self, ..)` or `Ord::cmp(self, ..)`. fn self_cmp_call<'tcx>( cx: &LateContext<'tcx>, + typeck: &TypeckResults<'tcx>, cmp_expr: &'tcx Expr<'tcx>, - def_id: LocalDefId, needs_fully_qualified: &mut bool, ) -> bool { match cmp_expr.kind { - ExprKind::Call(path, [_, _]) => path_res(cx, path) + ExprKind::Call(path, [_, _]) => path + .res(typeck) .opt_def_id() .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::ord_cmp_method, def_id)), ExprKind::MethodCall(_, recv, [_], ..) => { @@ -309,11 +307,7 @@ fn self_cmp_call<'tcx>( // `else` branch, it must be a method named `cmp` that isn't `Ord::cmp` *needs_fully_qualified = true; - // It's a bit annoying but `typeck_results` only gives us the CURRENT body, which we - // have none, not of any `LocalDefId` we want, so we must call the query itself to avoid - // an immediate ICE - cx.tcx - .typeck(def_id) + typeck .type_dependent_def_id(cmp_expr.hir_id) .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::ord_cmp_method, def_id)) }, diff --git a/clippy_lints/src/partialeq_to_none.rs b/clippy_lints/src/partialeq_to_none.rs index e6e4920e7d2c0..89cfd352bfe82 100644 --- a/clippy_lints/src/partialeq_to_none.rs +++ b/clippy_lints/src/partialeq_to_none.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; -use clippy_utils::{is_res_lang_ctor, path_res, peel_hir_expr_refs, peel_ref_operators, sugg}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::{is_res_lang_ctor, peel_hir_expr_refs, peel_ref_operators, sugg}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; @@ -57,7 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialeqToNone { // If the expression is a literal `Option::None` let is_none_ctor = |expr: &Expr<'_>| { !expr.span.from_expansion() - && is_res_lang_ctor(cx, path_res(cx, peel_hir_expr_refs(expr).0), LangItem::OptionNone) + && is_res_lang_ctor(cx, peel_hir_expr_refs(expr).0.res(cx), LangItem::OptionNone) }; let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 8b2ecece8373f..05af8fe016d13 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -11,7 +11,7 @@ use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{ eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, - is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, peel_blocks, + is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, span_contains_cfg, span_contains_comment, sym, }; use rustc_errors::Applicability; @@ -394,7 +394,7 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A // check `=> return Err(...)` && let ExprKind::Ret(Some(wrapped_ret_expr)) = arm_body.kind && let ExprKind::Call(ok_ctor, [ret_expr]) = wrapped_ret_expr.kind - && is_res_lang_ctor(cx, path_res(cx, ok_ctor), ResultErr) + && is_res_lang_ctor(cx, ok_ctor.res(cx), ResultErr) // check if `...` is `val` from binding or `val.into()` && is_local_or_local_into(cx, ret_expr, ok_val) { @@ -405,10 +405,10 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A }, TryMode::Option => { // Check the pat is `None` - if is_res_lang_ctor(cx, path_res(cx, arm.pat), OptionNone) + if is_res_lang_ctor(cx, arm.pat.res(cx), OptionNone) // Check `=> return None` && let ExprKind::Ret(Some(ret_expr)) = arm_body.kind - && is_res_lang_ctor(cx, path_res(cx, ret_expr), OptionNone) + && is_res_lang_ctor(cx, ret_expr.res(cx), OptionNone) && !ret_expr.span.from_expansion() { true diff --git a/clippy_lints/src/returns/needless_return_with_question_mark.rs b/clippy_lints/src/returns/needless_return_with_question_mark.rs index c05038cd1e50e..f6bbb75f98a34 100644 --- a/clippy_lints/src/returns/needless_return_with_question_mark.rs +++ b/clippy_lints/src/returns/needless_return_with_question_mark.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res}; +use clippy_utils::res::MaybeQPath; +use clippy_utils::{is_from_proc_macro, is_inside_let_else, is_res_lang_ctor}; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; use rustc_hir::{ExprKind, HirId, ItemKind, MatchSource, Node, OwnerNode, Stmt, StmtKind}; @@ -19,7 +20,7 @@ pub(super) fn check_stmt<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { && let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind && let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind && let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind - && is_res_lang_ctor(cx, path_res(cx, maybe_constr), ResultErr) + && is_res_lang_ctor(cx, maybe_constr.res(cx), ResultErr) // Ensure this is not the final stmt, otherwise removing it would cause a compile error && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) diff --git a/clippy_lints/src/single_option_map.rs b/clippy_lints/src/single_option_map.rs index b4375d4f9b042..4556d287711fe 100644 --- a/clippy_lints/src/single_option_map.rs +++ b/clippy_lints/src/single_option_map.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::res::MaybeDef; -use clippy_utils::{path_res, peel_blocks}; +use clippy_utils::peel_blocks; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; @@ -57,8 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for SingleOptionMap { && let callee_type = cx.typeck_results().expr_ty(callee) && callee_type.is_diag_item(cx, sym::Option) && let ExprKind::Path(_path) = callee.kind - && let Res::Local(_id) = path_res(cx, callee) - && matches!(path_res(cx, callee), Res::Local(_id)) + && matches!(callee.basic_res(), Res::Local(_)) && !matches!(args[0].kind, ExprKind::Path(_)) { if let ExprKind::Closure(closure) = args[0].kind { @@ -71,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for SingleOptionMap { } else if let ExprKind::MethodCall(_segment, receiver, method_args, _span) = value.kind && matches!(receiver.kind, ExprKind::Path(_)) && method_args.iter().all(|arg| matches!(arg.kind, ExprKind::Path(_))) - && method_args.iter().all(|arg| matches!(path_res(cx, arg), Res::Local(_))) + && method_args.iter().all(|arg| matches!(arg.basic_res(), Res::Local(_))) { return; } diff --git a/clippy_lints/src/unnecessary_literal_bound.rs b/clippy_lints/src/unnecessary_literal_bound.rs index 9f107fbeec03a..2603884440d16 100644 --- a/clippy_lints/src/unnecessary_literal_bound.rs +++ b/clippy_lints/src/unnecessary_literal_bound.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::path_res; +use clippy_utils::res::MaybeResPath; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -138,7 +138,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryLiteralBound { return; }; - if path_res(cx, inner_hir_ty) != Res::PrimTy(PrimTy::Str) { + if !matches!(inner_hir_ty.basic_res(), Res::PrimTy(PrimTy::Str)) { return; } diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 849c0b438a5a0..5adc99acf16fa 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -2,9 +2,10 @@ use std::borrow::Cow; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::snippet; use clippy_utils::visitors::find_all_ret_expressions; -use clippy_utils::{contains_return, is_res_lang_ctor, path_res, return_ty}; +use clippy_utils::{contains_return, is_res_lang_ctor, return_ty}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::intravisit::FnKind; @@ -122,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if !ret_expr.span.from_expansion() // Check if a function call. && let ExprKind::Call(func, [arg]) = ret_expr.kind - && is_res_lang_ctor(cx, path_res(cx, func), lang_item) + && is_res_lang_ctor(cx, func.res(cx), lang_item) // Make sure the function argument does not contain a return expression. && !contains_return(arg) { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 59b6b3271c44f..77367b7d2f6c6 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -471,17 +471,11 @@ pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option { } } -/// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err` -pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: impl MaybeQPath<'tcx>) -> Res { - match maybe_path.opt_qpath() { - None => Res::Err, - Some((qpath, id)) => cx.qpath_res(qpath, id), - } -} - /// If `maybe_path` is a path node which resolves to an item, retrieves the item ID pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: impl MaybeQPath<'tcx>) -> Option { - path_res(cx, maybe_path).opt_def_id() + maybe_path + .opt_qpath() + .and_then(|(qpath, id)| cx.qpath_res(qpath, id).opt_def_id()) } /// Gets the `hir::TraitRef` of the trait the given method is implemented for. diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 03f9f42c09687..bb2ef9fa4692b 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -34,9 +34,8 @@ use std::assert_matches::debug_assert_matches; use std::collections::hash_map::Entry; use std::{iter, mem}; -use crate::path_res; use crate::paths::{PathNS, lookup_path_str}; -use crate::res::MaybeDef; +use crate::res::{MaybeDef, MaybeQPath}; mod type_certainty; pub use type_certainty::expr_type_is_certain; @@ -579,7 +578,7 @@ impl<'tcx> ExprFnSig<'tcx> { /// If the expression is function like, get the signature for it. pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option> { - if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) { + if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = expr.res(cx) { Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).instantiate_identity(), Some(id))) } else { ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs()) From 3ed7aa0d5f121046bf907cac30453d3d9454f0d6 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 07:38:25 -0400 Subject: [PATCH 55/89] Remove `path_def_id` --- clippy_lints/src/box_default.rs | 7 ++++--- clippy_lints/src/explicit_write.rs | 5 +++-- clippy_lints/src/from_over_into.rs | 9 +++++++-- clippy_lints/src/from_raw_with_void_ptr.rs | 5 +++-- clippy_lints/src/methods/chars_cmp.rs | 5 +++-- clippy_lints/src/methods/option_map_or_none.rs | 4 ++-- clippy_lints/src/non_std_lazy_statics.rs | 5 +++-- clippy_lints/src/only_used_in_recursion.rs | 5 +++-- clippy_lints/src/operators/cmp_owned.rs | 15 +++++++++------ clippy_lints/src/ptr.rs | 9 ++++++--- clippy_lints/src/regex.rs | 5 +++-- clippy_lints/src/replace_box.rs | 7 ++++--- clippy_lints/src/size_of_ref.rs | 5 ++--- clippy_lints/src/strings.rs | 7 +++---- clippy_lints/src/swap_ptr_to_ref.rs | 5 ++--- clippy_lints/src/types/box_collection.rs | 5 +++-- clippy_lints/src/types/option_option.rs | 5 +++-- clippy_lints/src/types/rc_buffer.rs | 11 +++++------ clippy_lints/src/types/rc_mutex.rs | 6 +++--- clippy_lints/src/types/redundant_allocation.rs | 7 +++++-- clippy_lints/src/unconditional_recursion.rs | 5 +++-- clippy_lints/src/utils/author.rs | 4 ++-- clippy_lints_internal/src/unnecessary_def_path.rs | 5 +++-- clippy_utils/src/lib.rs | 11 ++--------- clippy_utils/src/paths.rs | 7 +++++-- 25 files changed, 91 insertions(+), 73 deletions(-) diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index 3b861848f94a6..6f51f2343ab5b 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_default_equivalent; use clippy_utils::macros::macro_backtrace; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::ty::expr_sig; -use clippy_utils::{is_default_equivalent, path_def_id}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty}; -use rustc_hir::{AmbigArg, Block, Expr, ExprKind, HirId, LetStmt, Node, QPath, Ty, TyKind}; +use rustc_hir::{AmbigArg, Block, Expr, ExprKind, HirId, LangItem, LetStmt, Node, QPath, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::{Span, sym}; @@ -44,7 +45,7 @@ impl LateLintPass<'_> for BoxDefault { // And that method is `new` && seg.ident.name == sym::new // And the call is that of a `Box` method - && path_def_id(cx, ty).is_some_and(|id| Some(id) == cx.tcx.lang_items().owned_box()) + && ty.basic_res().is_lang_item(cx, LangItem::OwnedBox) // And the single argument to the call is another function call // This is the `T::default()` (or default equivalent) of `Box::new(T::default())` && let ExprKind::Call(arg_path, _) = arg.kind diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 085ee4448a4ee..2a3b75ecae1b9 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_expn_of, path_def_id, sym}; +use clippy_utils::{is_expn_of, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{BindingMode, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind}; @@ -59,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { && let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = *look_in_block(cx, &write_call.kind) && let ExprKind::Call(write_recv_path, []) = write_recv.kind && write_fun.ident.name == sym::write_fmt - && let Some(def_id) = path_def_id(cx, write_recv_path) + && let Some(def_id) = write_recv_path.basic_res().opt_def_id() { // match calls to std::io::stdout() / std::io::stderr () let (dest_name, prefix) = match cx.tcx.get_diagnostic_name(def_id) { diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index e3bb5ee10db7c..fd807290dc090 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -4,7 +4,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::span_is_local; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::path_def_id; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_path}; @@ -90,7 +90,12 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { |diag| { // If the target type is likely foreign mention the orphan rules as it's a common source of // confusion - if path_def_id(cx, target_ty.peel_refs()).is_none_or(|id| !id.is_local()) { + if target_ty + .peel_refs() + .basic_res() + .opt_def_id() + .is_none_or(|id| !id.is_local()) + { diag.help( "`impl From for Foreign` is allowed by the orphan rules, for more information see\n\ https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence" diff --git a/clippy_lints/src/from_raw_with_void_ptr.rs b/clippy_lints/src/from_raw_with_void_ptr.rs index 5e2e2c9dbf725..0e6eeb75ec573 100644 --- a/clippy_lints/src/from_raw_with_void_ptr.rs +++ b/clippy_lints/src/from_raw_with_void_ptr.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::res::MaybeResPath; +use clippy_utils::sym; use clippy_utils::ty::is_c_void; -use clippy_utils::{path_def_id, sym}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; @@ -41,7 +42,7 @@ impl LateLintPass<'_> for FromRawWithVoidPtr { if let ExprKind::Call(box_from_raw, [arg]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind && seg.ident.name == sym::from_raw - && let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id)) + && let Some(type_str) = ty.basic_res().opt_def_id().and_then(|id| def_id_matches_type(cx, id)) && let arg_kind = cx.typeck_results().expr_ty(arg).kind() && let ty::RawPtr(ty, _) = arg_kind && is_c_void(cx, *ty) diff --git a/clippy_lints/src/methods/chars_cmp.rs b/clippy_lints/src/methods/chars_cmp.rs index de27a45ba4d92..4b0fc7eba8e34 100644 --- a/clippy_lints/src/methods/chars_cmp.rs +++ b/clippy_lints/src/methods/chars_cmp.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::method_chain_args; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{method_chain_args, path_def_id}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, Lint}; @@ -17,7 +18,7 @@ pub(super) fn check( ) -> bool { if let Some(args) = method_chain_args(info.chain, chain_methods) && let hir::ExprKind::Call(fun, [arg_char]) = info.other.kind - && let Some(id) = path_def_id(cx, fun).map(|ctor_id| cx.tcx.parent(ctor_id)) + && let Some(id) = fun.res(cx).opt_def_id().map(|ctor_id| cx.tcx.parent(ctor_id)) && Some(id) == cx.tcx.lang_items().option_some_variant() { let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index e52a8a0266d50..6e59fc7662f29 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_res_lang_ctor; use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; -use clippy_utils::{is_res_lang_ctor, path_def_id}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -62,7 +62,7 @@ pub(super) fn check<'tcx>( && let arg_snippet = snippet(cx, fn_decl_span, "..") && let body = cx.tcx.hir_body(body) && let Some((func, [arg_char])) = reduce_unit_expression(body.value) - && let Some(id) = path_def_id(cx, func).map(|ctor_id| cx.tcx.parent(ctor_id)) + && let Some(id) = func.res(cx).opt_def_id().map(|ctor_id| cx.tcx.parent(ctor_id)) && Some(id) == cx.tcx.lang_items().option_some_variant() { let func_snippet = snippet(cx, arg_char.span, ".."); diff --git a/clippy_lints/src/non_std_lazy_statics.rs b/clippy_lints/src/non_std_lazy_statics.rs index 7ecde40aee017..c9c272abf9e3b 100644 --- a/clippy_lints/src/non_std_lazy_statics.rs +++ b/clippy_lints/src/non_std_lazy_statics.rs @@ -2,8 +2,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_hir_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::paths::{self, PathNS, find_crates, lookup_path_str}; +use clippy_utils::res::MaybeResPath; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{fn_def_id, is_no_std_crate, path_def_id, sym}; +use clippy_utils::{fn_def_id, is_no_std_crate, sym}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -188,7 +189,7 @@ impl LazyInfo { fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Option { // Check if item is a `once_cell:sync::Lazy` static. if let ItemKind::Static(_, _, ty, body_id) = item.kind - && let Some(path_def_id) = path_def_id(cx, ty) + && let Some(path_def_id) = ty.basic_res().opt_def_id() && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind && paths::ONCE_CELL_SYNC_LAZY.matches(cx, path_def_id) { diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index b9cdae0f267ee..10cc4f48456fd 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{get_expr_use_or_unification_node, path_def_id, path_to_local, path_to_local_id}; +use clippy_utils::res::MaybeQPath; +use clippy_utils::{get_expr_use_or_unification_node, path_to_local, path_to_local_id}; use core::cell::Cell; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; @@ -370,7 +371,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { Some((Node::Expr(parent), child_id)) => match parent.kind { // Recursive call. Track which index the parameter is used in. ExprKind::Call(callee, args) - if path_def_id(cx, callee).is_some_and(|id| { + if callee.res(cx).opt_def_id().is_some_and(|id| { id == param.fn_id && has_matching_args(param.fn_kind, typeck.node_args(callee.hir_id)) }) => { diff --git a/clippy_lints/src/operators/cmp_owned.rs b/clippy_lints/src/operators/cmp_owned.rs index 604f8f5da0b86..05358de5b3482 100644 --- a/clippy_lints/src/operators/cmp_owned.rs +++ b/clippy_lints/src/operators/cmp_owned.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::path_def_id; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_errors::Applicability; @@ -47,11 +47,14 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) (arg, arg.span) }, ExprKind::Call(path, [arg]) - if path_def_id(cx, path).is_some_and(|did| match cx.tcx.get_diagnostic_name(did) { - Some(sym::from_str_method) => true, - Some(sym::from_fn) => !is_copy(cx, typeck.expr_ty(expr)), - _ => false, - }) => + if path + .res(cx) + .opt_def_id() + .is_some_and(|did| match cx.tcx.get_diagnostic_name(did) { + Some(sym::from_str_method) => true, + Some(sym::from_fn) => !is_copy(cx, typeck.expr_ty(expr)), + _ => false, + }) => { (arg, arg.span) }, diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 9eed46460a613..ec82817ef2bdb 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::SpanRangeExt; use clippy_utils::sugg::Sugg; use clippy_utils::visitors::contains_unsafe_block; -use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, std_or_core, sym}; +use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_to_local, std_or_core, sym}; use hir::LifetimeKind; use rustc_abi::ExternAbi; use rustc_errors::{Applicability, MultiSpan}; @@ -742,8 +743,10 @@ fn get_lifetimes<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Vec<(&'tcx Lifetime, Option, expr: &Expr<'_>) -> bool { if let ExprKind::Call(pathexp, []) = expr.kind { - path_def_id(cx, pathexp) - .is_some_and(|id| matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut))) + matches!( + pathexp.basic_res().opt_diag_name(cx), + Some(sym::ptr_null | sym::ptr_null_mut) + ) } else { false } diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 89d945161f629..d1fc228f4b352 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -2,9 +2,10 @@ use std::fmt::Display; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::paths; use clippy_utils::paths::PathLookup; +use clippy_utils::res::MaybeQPath; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{path_def_id, paths}; use rustc_ast::ast::{LitKind, StrStyle}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{BorrowKind, Expr, ExprKind, OwnerId}; @@ -138,7 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for Regex { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Call(fun, [arg]) = expr.kind - && let Some(def_id) = path_def_id(cx, fun) + && let Some(def_id) = fun.res(cx).opt_def_id() && let Some(regex_kind) = self.definitions.get(&def_id) { if let Some(&(loop_item_id, loop_span)) = self.loop_stack.last() diff --git a/clippy_lints/src/replace_box.rs b/clippy_lints/src/replace_box.rs index 9ad61f25dfcc4..b838613f89a0f 100644 --- a/clippy_lints/src/replace_box.rs +++ b/clippy_lints/src/replace_box.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_default_equivalent_call, local_is_initialized, path_def_id, path_to_local}; +use clippy_utils::{is_default_equivalent_call, local_is_initialized, path_to_local}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_hir::{Expr, ExprKind, LangItem, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -101,7 +102,7 @@ fn get_box_new_payload<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option< if let ExprKind::Call(box_new, [arg]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_new.kind && seg.ident.name == sym::new - && path_def_id(cx, ty).is_some_and(|id| Some(id) == cx.tcx.lang_items().owned_box()) + && ty.basic_res().is_lang_item(cx, LangItem::OwnedBox) { Some(arg) } else { diff --git a/clippy_lints/src/size_of_ref.rs b/clippy_lints/src/size_of_ref.rs index 606e852aae9e3..bf304ebcfdc05 100644 --- a/clippy_lints/src/size_of_ref.rs +++ b/clippy_lints/src/size_of_ref.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::path_def_id; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::ty::peel_and_count_ty_refs; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -57,8 +57,7 @@ declare_lint_pass!(SizeOfRef => [SIZE_OF_REF]); impl LateLintPass<'_> for SizeOfRef { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { if let ExprKind::Call(path, [arg]) = expr.kind - && let Some(def_id) = path_def_id(cx, path) - && cx.tcx.is_diagnostic_item(sym::mem_size_of_val, def_id) + && path.basic_res().is_diag_item(cx, sym::mem_size_of_val) && let arg_ty = cx.typeck_results().expr_ty(arg) && peel_and_count_ty_refs(arg_ty).1 > 1 { diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index c70227cefbc73..47306949a6990 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::{ - SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, method_calls, path_def_id, - peel_blocks, sym, + SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, method_calls, peel_blocks, sym, }; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -256,7 +255,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { if let ExprKind::Call(fun, [bytes_arg]) = e.kind // Find `std::str::converts::from_utf8` or `std::primitive::str::from_utf8` && let Some(sym::str_from_utf8 | sym::str_inherent_from_utf8) = - path_def_id(cx, fun).and_then(|id| cx.tcx.get_diagnostic_name(id)) + fun.res(cx).opt_diag_name(cx) // Find string::as_bytes && let ExprKind::AddrOf(BorrowKind::Ref, _, args) = bytes_arg.kind diff --git a/clippy_lints/src/swap_ptr_to_ref.rs b/clippy_lints/src/swap_ptr_to_ref.rs index ff196355a2e37..339c97d575aee 100644 --- a/clippy_lints/src/swap_ptr_to_ref.rs +++ b/clippy_lints/src/swap_ptr_to_ref.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::path_def_id; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, UnOp}; @@ -41,8 +41,7 @@ declare_lint_pass!(SwapPtrToRef => [SWAP_PTR_TO_REF]); impl LateLintPass<'_> for SwapPtrToRef { fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { if let ExprKind::Call(fn_expr, [arg1, arg2]) = e.kind - && let Some(fn_id) = path_def_id(cx, fn_expr) - && cx.tcx.is_diagnostic_item(sym::mem_swap, fn_id) + && fn_expr.basic_res().is_diag_item(cx, sym::mem_swap) && let ctxt = e.span.ctxt() && let (from_ptr1, arg1_span) = is_ptr_to_ref(cx, arg1, ctxt) && let (from_ptr2, arg2_span) = is_ptr_to_ref(cx, arg2, ctxt) diff --git a/clippy_lints/src/types/box_collection.rs b/clippy_lints/src/types/box_collection.rs index 24fe4e08a5b44..985057cd6734b 100644 --- a/clippy_lints/src/types/box_collection.rs +++ b/clippy_lints/src/types/box_collection.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{path_def_id, qpath_generic_tys}; +use clippy_utils::qpath_generic_tys; +use clippy_utils::res::MaybeResPath; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; @@ -33,7 +34,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option { let param = qpath_generic_tys(qpath).next()?; - let id = path_def_id(cx, param)?; + let id = param.basic_res().opt_def_id()?; cx.tcx .get_diagnostic_name(id) .filter(|&name| { diff --git a/clippy_lints/src/types/option_option.rs b/clippy_lints/src/types/option_option.rs index d12d14f2b1413..10df007f2a13d 100644 --- a/clippy_lints/src/types/option_option.rs +++ b/clippy_lints/src/types/option_option.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{path_def_id, qpath_generic_tys}; +use clippy_utils::qpath_generic_tys; +use clippy_utils::res::MaybeResPath; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; @@ -10,7 +11,7 @@ use super::OPTION_OPTION; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { if cx.tcx.is_diagnostic_item(sym::Option, def_id) && let Some(arg) = qpath_generic_tys(qpath).next() - && path_def_id(cx, arg) == Some(def_id) + && arg.basic_res().opt_def_id() == Some(def_id) { span_lint( cx, diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs index c4fd0fbf87a90..46d9febb187fc 100644 --- a/clippy_lints/src/types/rc_buffer.rs +++ b/clippy_lints/src/types/rc_buffer.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::qpath_generic_tys; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath, TyKind}; @@ -28,8 +29,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ let Some(ty) = qpath_generic_tys(qpath).next() else { return false; }; - let Some(id) = path_def_id(cx, ty) else { return false }; - if !cx.tcx.is_diagnostic_item(sym::Vec, id) { + if !ty.basic_res().is_diag_item(cx, sym::Vec) { return false; } let TyKind::Path(qpath) = &ty.kind else { return false }; @@ -70,8 +70,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ }, ); } else if let Some(ty) = qpath_generic_tys(qpath).next() { - let Some(id) = path_def_id(cx, ty) else { return false }; - if !cx.tcx.is_diagnostic_item(sym::Vec, id) { + if !ty.basic_res().is_diag_item(cx, sym::Vec) { return false; } let TyKind::Path(qpath) = &ty.kind else { return false }; @@ -106,7 +105,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> { let ty = qpath_generic_tys(qpath).next()?; - let id = path_def_id(cx, ty)?; + let id = ty.basic_res().opt_def_id()?; let path = match cx.tcx.get_diagnostic_name(id) { Some(sym::OsString) => "std::ffi::OsStr", Some(sym::PathBuf) => "std::path::Path", diff --git a/clippy_lints/src/types/rc_mutex.rs b/clippy_lints/src/types/rc_mutex.rs index 7b13debc01ef8..e3d536822d5f8 100644 --- a/clippy_lints/src/types/rc_mutex.rs +++ b/clippy_lints/src/types/rc_mutex.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{path_def_id, qpath_generic_tys}; +use clippy_utils::qpath_generic_tys; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; @@ -10,8 +11,7 @@ use super::RC_MUTEX; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { if cx.tcx.is_diagnostic_item(sym::Rc, def_id) && let Some(arg) = qpath_generic_tys(qpath).next() - && let Some(id) = path_def_id(cx, arg) - && cx.tcx.is_diagnostic_item(sym::Mutex, id) + && arg.basic_res().is_diag_item(cx, sym::Mutex) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then(cx, RC_MUTEX, hir_ty.span, "usage of `Rc>`", |diag| { diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index 0ba51daf027d1..dbae2cc335165 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::qpath_generic_tys; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath, TyKind}; @@ -40,7 +41,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>, qpath: let Some(ty) = qpath_generic_tys(qpath).next() else { return false; }; - let Some(id) = path_def_id(cx, ty) else { return false }; + let Some(id) = ty.basic_res().opt_def_id() else { + return false; + }; let (inner_sym, ty) = match cx.tcx.get_diagnostic_name(id) { Some(sym::Arc) => ("Arc", ty), Some(sym::Rc) => ("Rc", ty), diff --git a/clippy_lints/src/unconditional_recursion.rs b/clippy_lints/src/unconditional_recursion.rs index e843e169113e7..e6663182fec53 100644 --- a/clippy_lints/src/unconditional_recursion.rs +++ b/clippy_lints/src/unconditional_recursion.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{expr_or_init, fn_def_id_with_node_args, path_def_id}; +use clippy_utils::res::MaybeQPath; +use clippy_utils::{expr_or_init, fn_def_id_with_node_args}; use rustc_ast::BinOpKind; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; @@ -317,7 +318,7 @@ where if let ExprKind::Call(f, _) = expr.kind && let ExprKind::Path(qpath) = f.kind && 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(method_def_id) = f.res(self.cx).opt_def_id() && let Some(trait_def_id) = self.cx.tcx.trait_of_assoc(method_def_id) && self.cx.tcx.is_diagnostic_item(sym::Default, trait_def_id) { diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index bfd115ec18d79..68e51dace2db2 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -1,5 +1,5 @@ use clippy_utils::res::MaybeQPath; -use clippy_utils::{get_attr, higher, path_def_id, sym}; +use clippy_utils::{get_attr, higher, sym}; use itertools::Itertools; use rustc_ast::LitIntType; use rustc_ast::ast::{LitFloatType, LitKind}; @@ -293,7 +293,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } fn maybe_path<'p>(&self, path: &Binding>) { - if let Some(id) = path_def_id(self.cx, path.value) + if let Some(id) = path.value.res(self.cx).opt_def_id() && !id.is_local() { if let Some(lang) = self.cx.tcx.lang_items().from_def_id(id) { diff --git a/clippy_lints_internal/src/unnecessary_def_path.rs b/clippy_lints_internal/src/unnecessary_def_path.rs index ced0a4b067bb9..f2e8d3579d851 100644 --- a/clippy_lints_internal/src/unnecessary_def_path.rs +++ b/clippy_lints_internal/src/unnecessary_def_path.rs @@ -1,7 +1,8 @@ use crate::internal_paths; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths::{PathNS, lookup_path}; -use clippy_utils::{path_def_id, peel_ref_operators}; +use clippy_utils::peel_ref_operators; +use clippy_utils::res::MaybeQPath; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -53,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { let path: Vec = segments .iter() .map(|segment| { - if let Some(const_def_id) = path_def_id(cx, segment) + if let Some(const_def_id) = segment.res(cx).opt_def_id() && let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(const_def_id) && let Some(value) = value.to_u32().discard_err() { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 77367b7d2f6c6..db4edb71d065a 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -132,7 +132,7 @@ use crate::ast_utils::unordered_over; use crate::consts::{ConstEvalCtxt, Constant}; use crate::higher::Range; use crate::msrvs::Msrv; -use crate::res::{MaybeDef, MaybeQPath, MaybeResPath}; +use crate::res::{MaybeDef, MaybeResPath}; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -471,13 +471,6 @@ pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option { } } -/// If `maybe_path` is a path node which resolves to an item, retrieves the item ID -pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: impl MaybeQPath<'tcx>) -> Option { - maybe_path - .opt_qpath() - .and_then(|(qpath, id)| cx.qpath_res(qpath, id).opt_def_id()) -} - /// Gets the `hir::TraitRef` of the trait the given method is implemented for. /// /// Use this if you want to find the `TraitRef` of the `Add` trait in this example: @@ -1994,7 +1987,7 @@ pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir_body(body)), - _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)), + _ => expr.basic_res().is_diag_item(cx, sym::convert_identity), } } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index ff7c26d851bbe..b6832b384d4a8 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -5,7 +5,7 @@ //! See for more information. use crate::res::MaybeQPath; -use crate::{path_def_id, sym}; +use crate::sym; use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS}; @@ -98,7 +98,10 @@ impl PathLookup { /// Resolves `maybe_path` to a [`DefId`] and checks if the [`PathLookup`] matches it pub fn matches_path<'tcx>(&self, cx: &LateContext<'_>, maybe_path: impl MaybeQPath<'tcx>) -> bool { - path_def_id(cx, maybe_path).is_some_and(|def_id| self.matches(cx, def_id)) + maybe_path + .res(cx) + .opt_def_id() + .is_some_and(|def_id| self.matches(cx, def_id)) } /// Checks if the path resolves to `ty`'s definition, must be an `Adt` From 3f686a074d6dd94f954b1a992f535b5dff5678e2 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 08:17:00 -0400 Subject: [PATCH 56/89] Remove `is_res_lang_ctor` --- clippy_lints/src/if_then_some_else_none.rs | 9 ++-- clippy_lints/src/loops/manual_find.rs | 8 ++-- .../src/loops/while_let_on_iterator.rs | 5 ++- clippy_lints/src/match_result_ok.rs | 4 +- clippy_lints/src/matches/collapsible_match.rs | 9 ++-- clippy_lints/src/matches/manual_filter.rs | 6 +-- clippy_lints/src/matches/manual_map.rs | 6 +-- clippy_lints/src/matches/manual_ok_err.rs | 13 +++--- clippy_lints/src/matches/manual_utils.rs | 20 ++++++--- clippy_lints/src/matches/match_as_ref.rs | 11 +++-- clippy_lints/src/matches/needless_match.rs | 5 +-- clippy_lints/src/matches/try_err.rs | 6 +-- clippy_lints/src/mem_replace.rs | 8 ++-- .../iter_on_single_or_empty_collections.rs | 15 +++++-- clippy_lints/src/methods/manual_ok_or.rs | 15 +++++-- .../src/methods/option_map_or_none.rs | 5 +-- clippy_lints/src/methods/or_then_unwrap.rs | 3 +- .../src/methods/result_map_or_else_none.rs | 6 +-- .../src/methods/unnecessary_filter_map.rs | 15 ++++--- .../src/methods/unnecessary_literal_unwrap.rs | 12 ++--- clippy_lints/src/non_canonical_impls.rs | 6 +-- clippy_lints/src/option_if_let_else.rs | 16 ++++--- clippy_lints/src/partialeq_to_none.rs | 8 +++- clippy_lints/src/question_mark.rs | 26 ++++++----- .../needless_return_with_question_mark.rs | 6 +-- clippy_lints/src/unnecessary_wraps.rs | 6 +-- clippy_lints/src/unused_io_amount.rs | 10 +++-- clippy_utils/src/lib.rs | 45 ++++++++++--------- 28 files changed, 181 insertions(+), 123 deletions(-) diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 11338e1536242..dfc8411baa005 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -2,12 +2,11 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, - peel_blocks, sym, + contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, peel_blocks, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -74,8 +73,8 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind && !expr.span.from_expansion() && !then_expr.span.from_expansion() - && is_res_lang_ctor(cx, then_call.res(cx), OptionSome) - && is_res_lang_ctor(cx, peel_blocks(els).res(cx), OptionNone) + && then_call.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) + && peel_blocks(els).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) && !is_else_clause(cx.tcx, expr) && !is_in_const_context(cx) && self.msrv.meets(cx, msrvs::BOOL_THEN) diff --git a/clippy_lints/src/loops/manual_find.rs b/clippy_lints/src/loops/manual_find.rs index 5fb40413955cb..c38cf83f44100 100644 --- a/clippy_lints/src/loops/manual_find.rs +++ b/clippy_lints/src/loops/manual_find.rs @@ -1,11 +1,11 @@ use super::MANUAL_FIND; use super::utils::make_iterator_snippet; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::res::{MaybeQPath, MaybeResPath}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{higher, is_res_lang_ctor, peel_blocks_with_stmt}; +use clippy_utils::{higher, peel_blocks_with_stmt}; use rustc_errors::Applicability; use rustc_hir::lang_items::LangItem; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind}; @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>( && let StmtKind::Semi(semi) = stmt.kind && let ExprKind::Ret(Some(ret_value)) = semi.kind && let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind - && is_res_lang_ctor(cx, ctor.res(cx), LangItem::OptionSome) + && ctor.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) && inner_ret.res_local_id() == Some(binding_id) && !contains_return_break_continue_macro(cond) && let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr) @@ -150,7 +150,7 @@ fn last_stmt_and_ret<'tcx>( && let Some((_, Node::Block(block))) = parent_iter.next() && let Some((last_stmt, last_ret)) = extract(block) && last_stmt.hir_id == node_hir - && is_res_lang_ctor(cx, last_ret.res(cx), LangItem::OptionNone) + && last_ret.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionNone) && let Some((_, Node::Expr(_block))) = parent_iter.next() // This includes the function header && let Some((_, func)) = parent_iter.next() diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 6000ff7a36099..df8b4d1551d42 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -2,9 +2,10 @@ use std::ops::ControlFlow; use super::WHILE_LET_ON_ITERATOR; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::visitors::is_res_used; -use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable, is_res_lang_ctor, is_trait_method}; +use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable, is_trait_method}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{Visitor, walk_expr}; @@ -19,7 +20,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some(higher::WhileLet { if_then, let_pat, let_expr, label, .. }) = higher::WhileLet::hir(expr) // check for `Some(..)` pattern && let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind - && is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome) + && cx.qpath_res(pat_path, let_pat.hir_id).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) // check for call to `Iterator::next` && let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind && method_name.ident.name == sym::next diff --git a/clippy_lints/src/match_result_ok.rs b/clippy_lints/src/match_result_ok.rs index bc9279677b2eb..fb83f7cf65ddb 100644 --- a/clippy_lints/src/match_result_ok.rs +++ b/clippy_lints/src/match_result_ok.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::{higher, is_res_lang_ctor, sym}; +use clippy_utils::{higher, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { && let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind //get operation && ok_path.ident.name == sym::ok && cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) - && is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome) + && cx.qpath_res(pat_path, let_pat.hir_id).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) && let ctxt = expr.span.ctxt() && let_expr.span.ctxt() == ctxt && let_pat.span.ctxt() == ctxt diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index c40f7620d945b..347d4f9fa4b5b 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::msrvs::Msrv; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; use clippy_utils::{ - SpanlessEq, get_ref_operators, is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, - peel_ref_operators, + SpanlessEq, get_ref_operators, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, }; use rustc_ast::BorrowKind; use rustc_errors::MultiSpan; @@ -136,7 +136,10 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { kind: PatExprKind::Path(qpath), hir_id, .. - }) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone), + }) => cx + .qpath_res(qpath, *hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone), _ => false, } } diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index 138d733e46d22..227db74b9d78a 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::path_to_local_id; use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::visitors::contains_unsafe_block; -use clippy_utils::{is_res_lang_ctor, path_to_local_id}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind}; @@ -66,7 +66,7 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: && let ExprKind::Call(callee, [arg]) = inner_expr.kind { return ctxt == expr.span.ctxt() - && is_res_lang_ctor(cx, callee.res(cx), OptionSome) + && callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) && path_to_local_id(arg, target); } false @@ -74,7 +74,7 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { - return is_res_lang_ctor(cx, inner_expr.res(cx), OptionNone); + return inner_expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone); } false } diff --git a/clippy_lints/src/matches/manual_map.rs b/clippy_lints/src/matches/manual_map.rs index 602a3ab1bb9d0..f111da60bbd5b 100644 --- a/clippy_lints/src/matches/manual_map.rs +++ b/clippy_lints/src/matches/manual_map.rs @@ -2,9 +2,7 @@ use super::MANUAL_MAP; use super::manual_utils::{SomeExpr, check_with}; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_res_lang_ctor; - -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use rustc_hir::LangItem::OptionSome; use rustc_hir::{Arm, Block, BlockCheckMode, Expr, ExprKind, Pat, UnsafeSource}; use rustc_lint::LateContext; @@ -92,7 +90,7 @@ fn get_some_expr<'tcx>( // TODO: Allow more complex expressions. match expr.kind { ExprKind::Call(callee, [arg]) - if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, callee.res(cx), OptionSome) => + if ctxt == expr.span.ctxt() && callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) => { Some(SomeExpr::new_no_negated(arg, needs_unsafe_block)) }, diff --git a/clippy_lints/src/matches/manual_ok_err.rs b/clippy_lints/src/matches/manual_ok_err.rs index fa2ab773c5393..c9293412fba82 100644 --- a/clippy_lints/src/matches/manual_ok_err.rs +++ b/clippy_lints/src/matches/manual_ok_err.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{indent_of, reindent_multiline}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{option_arg_ty, peel_and_count_ty_refs}; -use clippy_utils::{get_parent_expr, is_res_lang_ctor, peel_blocks, span_contains_comment}; +use clippy_utils::{get_parent_expr, peel_blocks, span_contains_comment}; use rustc_ast::{BindingMode, Mutability}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr}; @@ -73,7 +73,10 @@ fn is_variant_or_wildcard(cx: &LateContext<'_>, pat: &Pat<'_>, can_be_wild: bool true }, PatKind::TupleStruct(qpath, ..) => { - is_res_lang_ctor(cx, cx.qpath_res(&qpath, pat.hir_id), ResultErr) == must_match_err + cx.qpath_res(&qpath, pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, ResultErr) + == must_match_err }, PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) => { is_variant_or_wildcard(cx, pat, can_be_wild, must_match_err) @@ -104,7 +107,7 @@ fn is_ok_or_err<'hir>(cx: &LateContext<'_>, pat: &Pat<'hir>) -> Option<(bool, &' /// Check if `expr` contains `Some(ident)`, possibly as a block fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, ty: Ty<'tcx>) -> bool { if let ExprKind::Call(body_callee, [body_arg]) = peel_blocks(expr).kind - && is_res_lang_ctor(cx, body_callee.res(cx), OptionSome) + && body_callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) && cx.typeck_results().expr_ty(body_arg) == ty && let ExprKind::Path(QPath::Resolved( _, @@ -121,7 +124,7 @@ fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, t /// Check if `expr` is `None`, possibly as a block fn is_none(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - is_res_lang_ctor(cx, peel_blocks(expr).res(cx), OptionNone) + peel_blocks(expr).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) } /// Suggest replacing `expr` by `scrutinee.METHOD()`, where `METHOD` is either `ok` or diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index 9477e98acde5c..60925db893dae 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -5,8 +5,8 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_copy, is_unsafe_fn, peel_and_count_ty_refs}; use clippy_utils::{ - CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, is_res_lang_ctor, - path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, + CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, path_to_local_id, + peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; @@ -259,9 +259,19 @@ pub(super) fn try_parse_pattern<'tcx>( kind: PatExprKind::Path(qpath), hir_id, .. - }) if is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone) => Some(OptionPat::None), + }) if cx + .qpath_res(qpath, *hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone) => + { + Some(OptionPat::None) + }, PatKind::TupleStruct(ref qpath, [pattern], _) - if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt => + if cx + .qpath_res(qpath, pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionSome) + && pat.span.ctxt() == ctxt => { Some(OptionPat::Some { pattern, ref_count }) }, @@ -273,5 +283,5 @@ pub(super) fn try_parse_pattern<'tcx>( // Checks for the `None` value. fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - is_res_lang_ctor(cx, peel_blocks(expr).res(cx), OptionNone) + peel_blocks(expr).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) } diff --git a/clippy_lints/src/matches/match_as_ref.rs b/clippy_lints/src/matches/match_as_ref.rs index 3e236e82635f6..156118be347f9 100644 --- a/clippy_lints/src/matches/match_as_ref.rs +++ b/clippy_lints/src/matches/match_as_ref.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_none_arm, is_res_lang_ctor, peel_blocks}; +use clippy_utils::{is_none_arm, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath}; use rustc_lint::LateContext; @@ -59,10 +59,13 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind - && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionSome) + && cx + .qpath_res(qpath, arm.pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, LangItem::OptionSome) && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., ident, _) = first_pat.kind && let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind - && is_res_lang_ctor(cx, e.res(cx), LangItem::OptionSome) + && e.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) && let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind && path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index a13f9f35eea3e..c9b6821ad98fd 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -4,8 +4,7 @@ use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::same_type_modulo_regions; use clippy_utils::{ - SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, is_res_lang_ctor, over, - peel_blocks_with_stmt, + SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, over, peel_blocks_with_stmt, }; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; @@ -106,7 +105,7 @@ fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool } let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr); if let_expr_ty.is_diag_item(cx, sym::Option) { - return is_res_lang_ctor(cx, else_expr.res(cx), OptionNone) + return else_expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) || eq_expr_value(cx, if_let.let_expr, else_expr); } return eq_expr_value(cx, if_let.let_expr, else_expr); diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs index 4fd4851eab60a..7358628f0f7e6 100644 --- a/clippy_lints/src/matches/try_err.rs +++ b/clippy_lints/src/matches/try_err.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::res::MaybeQPath; +use clippy_utils::get_parent_expr; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::option_arg_ty; -use clippy_utils::{get_parent_expr, is_res_lang_ctor}; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -26,7 +26,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine && let ExprKind::Path(ref match_fun_path) = match_fun.kind && matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)) && let ExprKind::Call(err_fun, [err_arg]) = try_arg.kind - && is_res_lang_ctor(cx, err_fun.res(cx), ResultErr) + && err_fun.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) && let Some(return_ty) = find_return_type(cx, &expr.kind) { let (prefix, suffix, err_ty) = if let Some(ty) = result_error_type(cx, return_ty) { diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 7d042cd490fde..ac3cbaec55f30 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,11 +1,11 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_non_aggregate_primitive_type; -use clippy_utils::{is_default_equivalent, is_expr_used_or_unified, is_res_lang_ctor, peel_ref_operators, std_or_core}; +use clippy_utils::{is_default_equivalent, is_expr_used_or_unified, peel_ref_operators, std_or_core}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; @@ -128,7 +128,7 @@ impl_lint_pass!(MemReplace => [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_OPTION_WITH_SOME, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]); fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) -> bool { - if is_res_lang_ctor(cx, src.res(cx), OptionNone) { + if src.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) { // Since this is a late pass (already type-checked), // and we already know that the second argument is an // `Option`, we do not need to check the first @@ -162,7 +162,7 @@ fn check_replace_option_with_some( msrv: Msrv, ) -> bool { if let ExprKind::Call(src_func, [src_arg]) = src.kind - && is_res_lang_ctor(cx, src_func.res(cx), OptionSome) + && src_func.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) && msrv.meets(cx, msrvs::OPTION_REPLACE) { // We do not have to check for a `const` context here, because `core::mem::replace()` and diff --git a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 728240c724b48..8183c30f8c56b 100644 --- a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -1,10 +1,10 @@ use std::iter::once; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; use clippy_utils::ty::{ExprFnSig, expr_sig, ty_sig}; -use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, std_or_core, sym}; +use clippy_utils::{get_expr_use_or_unification_node, std_or_core, sym}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -68,8 +68,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method let item = match recv.kind { ExprKind::Array([]) => None, ExprKind::Array([e]) => Some(e), - ExprKind::Path(ref p) if is_res_lang_ctor(cx, cx.qpath_res(p, recv.hir_id), OptionNone) => None, - ExprKind::Call(f, [arg]) if is_res_lang_ctor(cx, f.res(cx), OptionSome) => Some(arg), + ExprKind::Path(ref p) + if cx + .qpath_res(p, recv.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone) => + { + None + }, + ExprKind::Call(f, [arg]) if f.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) => Some(arg), _ => return, }; let iter_type = match method_name { diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs index d84f188613fb3..80b955829f1fd 100644 --- a/clippy_lints/src/methods/manual_ok_or.rs +++ b/clippy_lints/src/methods/manual_ok_or.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::path_to_local_id; use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; -use clippy_utils::{is_res_lang_ctor, path_to_local_id}; use rustc_errors::Applicability; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{Expr, ExprKind, PatKind}; @@ -25,7 +25,7 @@ pub(super) fn check<'tcx>( .instantiate_identity() .is_diag_item(cx, sym::Option) && let ExprKind::Call(err_path, [err_arg]) = or_expr.kind - && is_res_lang_ctor(cx, err_path.res(cx), ResultErr) + && err_path.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) && is_ok_wrapping(cx, map_expr) && let Some(recv_snippet) = recv.span.get_source_text(cx) && let Some(err_arg_snippet) = err_arg.span.get_source_text(cx) @@ -46,12 +46,19 @@ pub(super) fn check<'tcx>( fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { match map_expr.kind { - ExprKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, map_expr.hir_id), ResultOk) => true, + ExprKind::Path(ref qpath) + if cx + .qpath_res(qpath, map_expr.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, ResultOk) => + { + true + }, ExprKind::Closure(closure) => { let body = cx.tcx.hir_body(closure.body); if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind && let ExprKind::Call(callee, [ok_arg]) = body.value.kind - && is_res_lang_ctor(cx, callee.res(cx), ResultOk) + && callee.res(cx).ctor_parent(cx).is_lang_item(cx, ResultOk) { path_to_local_id(ok_arg, param_id) } else { diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 6e59fc7662f29..342ffea51d656 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_res_lang_ctor; use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; use rustc_errors::Applicability; @@ -49,12 +48,12 @@ pub(super) fn check<'tcx>( return; } - if !is_res_lang_ctor(cx, def_arg.res(cx), OptionNone) { + if !def_arg.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) { // nothing to lint! return; } - let f_arg_is_some = is_res_lang_ctor(cx, map_arg.res(cx), OptionSome); + let f_arg_is_some = map_arg.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome); if is_option { let self_snippet = snippet(cx, recv.span, ".."); diff --git a/clippy_lints/src/methods/or_then_unwrap.rs b/clippy_lints/src/methods/or_then_unwrap.rs index 84b886d4fbe5a..07199b84f39ec 100644 --- a/clippy_lints/src/methods/or_then_unwrap.rs +++ b/clippy_lints/src/methods/or_then_unwrap.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_res_lang_ctor; use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; @@ -60,7 +59,7 @@ pub(super) fn check<'tcx>( fn get_content_if_ctor_matches(cx: &LateContext<'_>, expr: &Expr<'_>, item: LangItem) -> Option { if let ExprKind::Call(some_expr, [arg]) = expr.kind - && is_res_lang_ctor(cx, some_expr.res(cx), item) + && some_expr.res(cx).ctor_parent(cx).is_lang_item(cx, item) { Some(arg.span.source_callsite()) } else { diff --git a/clippy_lints/src/methods/result_map_or_else_none.rs b/clippy_lints/src/methods/result_map_or_else_none.rs index de0bd78c9354e..e2946c22a46b5 100644 --- a/clippy_lints/src/methods/result_map_or_else_none.rs +++ b/clippy_lints/src/methods/result_map_or_else_none.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::peel_blocks; use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; -use clippy_utils::{is_res_lang_ctor, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -21,11 +21,11 @@ pub(super) fn check<'tcx>( // lint if the caller of `map_or_else()` is a `Result` if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) // We check that it is mapped as `Some`. - && is_res_lang_ctor(cx, map_arg.res(cx), OptionSome) + && map_arg.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) && let hir::ExprKind::Closure(&hir::Closure { body, .. }) = def_arg.kind && let body = cx.tcx.hir_body(body) // And finally we check that we return a `None` in the "else case". - && is_res_lang_ctor(cx, peel_blocks(body.value).res(cx), OptionNone) + && peel_blocks(body.value).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) { let msg = "called `map_or_else(|_| None, Some)` on a `Result` value"; let self_snippet = snippet(cx, recv.span, ".."); diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index ac6a2e630cf31..cffb01f1bbc19 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,10 +1,10 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint; -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; -use clippy_utils::{is_res_lang_ctor, is_trait_method, path_to_local_id, sym}; +use clippy_utils::{is_trait_method, path_to_local_id, sym}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -47,7 +47,7 @@ pub(super) fn check<'tcx>( // Check if the closure is .filter_map(|x| Some(x)) if name == sym::filter_map && let hir::ExprKind::Call(expr, args) = body.value.kind - && is_res_lang_ctor(cx, expr.res(cx), OptionSome) + && expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) && let hir::ExprKind::Path(_) = args[0].kind { span_lint( @@ -96,7 +96,7 @@ pub(super) fn check<'tcx>( fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) { match expr.kind { hir::ExprKind::Call(func, args) => { - if is_res_lang_ctor(cx, func.res(cx), OptionSome) { + if func.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) { if path_to_local_id(&args[0], arg_id) { return (false, false); } @@ -134,7 +134,12 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc let else_check = check_expression(cx, arg_id, else_arm); (if_check.0 | else_check.0, if_check.1 | else_check.1) }, - hir::ExprKind::Path(ref path) if is_res_lang_ctor(cx, cx.qpath_res(path, expr.hir_id), OptionNone) => { + hir::ExprKind::Path(ref path) + if cx + .qpath_res(path, expr.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone) => + { (false, true) }, _ => (true, true), diff --git a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index dc5ce293aa8f3..410e973f855b6 100644 --- a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::res::MaybeQPath; -use clippy_utils::{is_res_lang_ctor, last_path_segment, sym}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::{last_path_segment, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, AmbigArg}; use rustc_lint::LateContext; @@ -45,16 +45,16 @@ pub(super) fn check( let args = last_path_segment(qpath).args.map(|args| args.args); let res = cx.qpath_res(qpath, hir_id); - if is_res_lang_ctor(cx, res, hir::LangItem::OptionSome) { + if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::OptionSome) { (sym::Some, call_args, get_ty_from_args(args, 0)) - } else if is_res_lang_ctor(cx, res, hir::LangItem::ResultOk) { + } else if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::ResultOk) { (sym::Ok, call_args, get_ty_from_args(args, 0)) - } else if is_res_lang_ctor(cx, res, hir::LangItem::ResultErr) { + } else if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::ResultErr) { (sym::Err, call_args, get_ty_from_args(args, 1)) } else { return; } - } else if is_res_lang_ctor(cx, init.res(cx), hir::LangItem::OptionNone) { + } else if init.res(cx).ctor_parent(cx).is_lang_item(cx, hir::LangItem::OptionNone) { let call_args: &[hir::Expr<'_>] = &[]; (sym::None, call_args, None) } else { diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index 0bb6a585789e2..0c5e4d279f5c7 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_diag_trait_item, is_from_proc_macro, is_res_lang_ctor, last_path_segment, std_or_core}; +use clippy_utils::{is_diag_trait_item, is_from_proc_macro, last_path_segment, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, LangItem, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -269,7 +269,7 @@ fn expr_is_cmp<'tcx>( }, [cmp_expr], ) => { - is_res_lang_ctor(cx, typeck.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) + typeck.qpath_res(some_path, *some_hir_id).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) // Fix #11178, allow `Self::cmp(self, ..)` && self_cmp_call(cx, typeck, cmp_expr, needs_fully_qualified) }, diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 3483f3081a58c..c32c74a8fe608 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,11 +1,12 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_copy; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, eager_or_lazy, expr_requires_coercion, higher, is_else_clause, - is_in_const_context, is_res_lang_ctor, peel_blocks, peel_hir_expr_while, + is_in_const_context, peel_blocks, peel_hir_expr_while, }; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -314,9 +315,9 @@ impl<'tcx> Visitor<'tcx> for ReferenceVisitor<'_, 'tcx> { fn try_get_inner_pat_and_is_result<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<(&'tcx Pat<'tcx>, bool)> { if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind { let res = cx.qpath_res(qpath, pat.hir_id); - if is_res_lang_ctor(cx, res, OptionSome) { + if res.ctor_parent(cx).is_lang_item(cx, OptionSome) { return Some((inner_pat, false)); - } else if is_res_lang_ctor(cx, res, ResultOk) { + } else if res.ctor_parent(cx).is_lang_item(cx, ResultOk) { return Some((inner_pat, true)); } } @@ -379,9 +380,14 @@ fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { kind: PatExprKind::Path(qpath), hir_id, .. - }) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone), + }) => cx + .qpath_res(qpath, *hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone), PatKind::TupleStruct(ref qpath, [first_pat], _) => { - is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr) + cx.qpath_res(qpath, arm.pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, ResultErr) && matches!(first_pat.kind, PatKind::Wild) }, PatKind::Wild => true, diff --git a/clippy_lints/src/partialeq_to_none.rs b/clippy_lints/src/partialeq_to_none.rs index 89cfd352bfe82..44217ac2c4fcb 100644 --- a/clippy_lints/src/partialeq_to_none.rs +++ b/clippy_lints/src/partialeq_to_none.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeQPath}; -use clippy_utils::{is_res_lang_ctor, peel_hir_expr_refs, peel_ref_operators, sugg}; +use clippy_utils::{peel_hir_expr_refs, peel_ref_operators, sugg}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; @@ -57,7 +57,11 @@ impl<'tcx> LateLintPass<'tcx> for PartialeqToNone { // If the expression is a literal `Option::None` let is_none_ctor = |expr: &Expr<'_>| { !expr.span.from_expansion() - && is_res_lang_ctor(cx, peel_hir_expr_refs(expr).0.res(cx), LangItem::OptionNone) + && peel_hir_expr_refs(expr) + .0 + .res(cx) + .ctor_parent(cx) + .is_lang_item(cx, LangItem::OptionNone) }; let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 05af8fe016d13..d0cce2b6d8751 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -11,8 +11,8 @@ use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{ eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, - is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, - peel_blocks_with_stmt, span_contains_cfg, span_contains_comment, sym, + pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, + span_contains_cfg, span_contains_comment, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; @@ -220,15 +220,15 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ sym::Option => { // We only need to check `if let Some(x) = option` not `if let None = option`, // because the later one will be suggested as `if option.is_none()` thus causing conflict. - is_res_lang_ctor(cx, res, OptionSome) + res.ctor_parent(cx).is_lang_item(cx, OptionSome) && if_else.is_some() && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, None) }, sym::Result => { - (is_res_lang_ctor(cx, res, ResultOk) + (res.ctor_parent(cx).is_lang_item(cx, ResultOk) && if_else.is_some() && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, Some(let_pat_sym))) - || is_res_lang_ctor(cx, res, ResultErr) + || res.ctor_parent(cx).is_lang_item(cx, ResultErr) && expr_return_none_or_err(smbl, cx, if_then, let_expr, Some(let_pat_sym)) && if_else.is_none() }, @@ -248,7 +248,10 @@ fn expr_return_none_or_err( match peel_blocks_with_stmt(expr).kind { ExprKind::Ret(Some(ret_expr)) => expr_return_none_or_err(smbl, cx, ret_expr, cond_expr, err_sym), ExprKind::Path(ref qpath) => match smbl { - sym::Option => is_res_lang_ctor(cx, cx.qpath_res(qpath, expr.hir_id), OptionNone), + sym::Option => cx + .qpath_res(qpath, expr.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone), sym::Result => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr), _ => false, }, @@ -343,7 +346,10 @@ fn extract_ctor_call<'a, 'tcx>( pat: &'a Pat<'tcx>, ) -> Option<&'a Pat<'tcx>> { if let PatKind::TupleStruct(variant_path, [val_binding], _) = &pat.kind - && is_res_lang_ctor(cx, cx.qpath_res(variant_path, pat.hir_id), expected_ctor) + && cx + .qpath_res(variant_path, pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, expected_ctor) { Some(val_binding) } else { @@ -394,7 +400,7 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A // check `=> return Err(...)` && let ExprKind::Ret(Some(wrapped_ret_expr)) = arm_body.kind && let ExprKind::Call(ok_ctor, [ret_expr]) = wrapped_ret_expr.kind - && is_res_lang_ctor(cx, ok_ctor.res(cx), ResultErr) + && ok_ctor.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) // check if `...` is `val` from binding or `val.into()` && is_local_or_local_into(cx, ret_expr, ok_val) { @@ -405,10 +411,10 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A }, TryMode::Option => { // Check the pat is `None` - if is_res_lang_ctor(cx, arm.pat.res(cx), OptionNone) + if arm.pat.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) // Check `=> return None` && let ExprKind::Ret(Some(ret_expr)) = arm_body.kind - && is_res_lang_ctor(cx, ret_expr.res(cx), OptionNone) + && ret_expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) && !ret_expr.span.from_expansion() { true diff --git a/clippy_lints/src/returns/needless_return_with_question_mark.rs b/clippy_lints/src/returns/needless_return_with_question_mark.rs index f6bbb75f98a34..c47a3ef21e869 100644 --- a/clippy_lints/src/returns/needless_return_with_question_mark.rs +++ b/clippy_lints/src/returns/needless_return_with_question_mark.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeQPath; -use clippy_utils::{is_from_proc_macro, is_inside_let_else, is_res_lang_ctor}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::{is_from_proc_macro, is_inside_let_else}; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; use rustc_hir::{ExprKind, HirId, ItemKind, MatchSource, Node, OwnerNode, Stmt, StmtKind}; @@ -20,7 +20,7 @@ pub(super) fn check_stmt<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { && let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind && let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind && let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind - && is_res_lang_ctor(cx, maybe_constr.res(cx), ResultErr) + && maybe_constr.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) // Ensure this is not the final stmt, otherwise removing it would cause a compile error && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 5adc99acf16fa..29747cf0e447a 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -2,10 +2,10 @@ use std::borrow::Cow; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; use clippy_utils::visitors::find_all_ret_expressions; -use clippy_utils::{contains_return, is_res_lang_ctor, return_ty}; +use clippy_utils::{contains_return, return_ty}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::intravisit::FnKind; @@ -123,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if !ret_expr.span.from_expansion() // Check if a function call. && let ExprKind::Call(func, [arg]) = ret_expr.kind - && is_res_lang_ctor(cx, func.res(cx), lang_item) + && func.res(cx).ctor_parent(cx).is_lang_item(cx, lang_item) // Make sure the function argument does not contain a return expression. && !contains_return(arg) { diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index af3ad4566c46a..cff798f7a89a0 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -1,6 +1,7 @@ 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, paths, peel_blocks, sym}; +use clippy_utils::res::MaybeDef; +use clippy_utils::{paths, peel_blocks, sym}; use hir::{ExprKind, HirId, PatKind}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -136,7 +137,10 @@ fn non_consuming_err_arm<'a>(cx: &LateContext<'a>, arm: &hir::Arm<'a>) -> bool { } if let PatKind::TupleStruct(ref path, [inner_pat], _) = arm.pat.kind { - return is_res_lang_ctor(cx, cx.qpath_res(path, inner_pat.hir_id), hir::LangItem::ResultErr); + return cx + .qpath_res(path, inner_pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, hir::LangItem::ResultErr); } false @@ -203,7 +207,7 @@ fn is_ok_wild_or_dotdot_pattern<'a>(cx: &LateContext<'a>, pat: &hir::Pat<'a>) -> if let PatKind::TupleStruct(ref path, inner_pat, _) = pat.kind // we check against Result::Ok to avoid linting on Err(_) or something else. - && is_res_lang_ctor(cx, cx.qpath_res(path, pat.hir_id), hir::LangItem::ResultOk) + && cx.qpath_res(path, pat.hir_id).ctor_parent(cx).is_lang_item(cx, hir::LangItem::ResultOk) { if matches!(inner_pat, []) { return true; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index db4edb71d065a..7a215d3916221 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -252,19 +252,6 @@ pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool { } } -/// Checks if a `Res` refers to a constructor of a `LangItem` -/// For example, use this to check whether a function call or a pattern is `Some(..)`. -pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool { - if let Res::Def(DefKind::Ctor(..), id) = res - && let Some(lang_id) = cx.tcx.lang_items().get(lang_item) - && let Some(id) = cx.tcx.opt_parent(id) - { - id == lang_id - } else { - false - } -} - /// Checks if `{ctor_call_id}(...)` is `{enum_item}::{variant_name}(...)`. pub fn is_enum_variant_ctor( cx: &LateContext<'_>, @@ -337,13 +324,17 @@ pub fn is_wild(pat: &Pat<'_>) -> bool { pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { matches!(pat.kind, PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) - if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone)) + if cx.qpath_res(qpath, pat.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone)) } /// Checks if `arm` has the form `None => None`. pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { is_none_pattern(cx, arm.pat) - && matches!(peel_blocks(arm.body).kind, ExprKind::Path(qpath) if is_res_lang_ctor(cx, cx.qpath_res(&qpath, arm.body.hir_id), OptionNone)) + && matches!( + peel_blocks(arm.body).kind, + ExprKind::Path(qpath) + if cx.qpath_res(&qpath, arm.body.hir_id).ctor_parent(cx).is_lang_item(cx, OptionNone) + ) } /// Checks if the given `QPath` belongs to a type alias. @@ -708,7 +699,10 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { }, ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func, Some(e)), ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg), - ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone), + ExprKind::Path(qpath) => cx + .qpath_res(qpath, e.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone), ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), ExprKind::Block(Block { stmts: [], expr, .. }, _) => expr.is_some_and(|e| is_default_equivalent(cx, e)), _ => false, @@ -1609,7 +1603,10 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind && ddpos.as_opt_usize().is_none() - && is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk) + && cx + .qpath_res(path, arm.pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, ResultOk) && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind && path_to_local_id(arm.body, hir_id) { @@ -1620,7 +1617,9 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind { - is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr) + cx.qpath_res(path, arm.pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, ResultErr) } else { false } @@ -2854,12 +2853,18 @@ pub fn pat_and_expr_can_be_question_mark<'a, 'hir>( else_body: &Expr<'_>, ) -> Option<&'a Pat<'hir>> { if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind - && is_res_lang_ctor(cx, cx.qpath_res(&pat_path, pat.hir_id), OptionSome) + && cx + .qpath_res(&pat_path, pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionSome) && !is_refutable(cx, inner_pat) && let else_body = peel_blocks(else_body) && let ExprKind::Ret(Some(ret_val)) = else_body.kind && let ExprKind::Path(ret_path) = ret_val.kind - && is_res_lang_ctor(cx, cx.qpath_res(&ret_path, ret_val.hir_id), OptionNone) + && cx + .qpath_res(&ret_path, ret_val.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionNone) { Some(inner_pat) } else { From 53675ce0617f2dde5b28efeaf1bd2a8fe6417d21 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 16:41:20 -0400 Subject: [PATCH 57/89] Remove `path_to_local` --- clippy_lints/src/assigning_clones.rs | 5 +++-- clippy_lints/src/casts/unnecessary_cast.rs | 7 ++++--- clippy_lints/src/dereference.rs | 4 ++-- clippy_lints/src/eta_reduction.rs | 8 +++----- clippy_lints/src/format_impl.rs | 5 +++-- .../src/functions/not_unsafe_ptr_arg_deref.rs | 5 +++-- clippy_lints/src/ifs/branches_sharing_code.rs | 6 +++--- clippy_lints/src/ifs/ifs_same_cond.rs | 5 +++-- clippy_lints/src/index_refutable_slice.rs | 5 +++-- clippy_lints/src/loops/manual_memcpy.rs | 11 ++++++----- clippy_lints/src/loops/mut_range_bound.rs | 5 +++-- clippy_lints/src/loops/same_item_push.rs | 8 ++++---- clippy_lints/src/loops/utils.rs | 5 +++-- clippy_lints/src/manual_float_methods.rs | 7 ++++--- clippy_lints/src/manual_is_ascii_check.rs | 5 +++-- clippy_lints/src/manual_rem_euclid.rs | 5 +++-- clippy_lints/src/matches/collapsible_match.rs | 8 +++----- clippy_lints/src/matches/match_same_arms.rs | 7 ++++--- clippy_lints/src/matches/redundant_guards.rs | 5 +++-- clippy_lints/src/methods/is_empty.rs | 6 ++++-- clippy_lints/src/methods/iter_skip_next.rs | 5 +++-- clippy_lints/src/methods/needless_collect.rs | 10 +++++----- .../src/methods/string_lit_chars_any.rs | 7 ++++--- .../src/methods/unnecessary_iter_cloned.rs | 5 +++-- .../src/mixed_read_write_in_expression.rs | 5 +++-- clippy_lints/src/needless_late_init.rs | 4 ++-- clippy_lints/src/no_effect.rs | 7 +++---- clippy_lints/src/only_used_in_recursion.rs | 6 +++--- clippy_lints/src/ptr.rs | 4 ++-- clippy_lints/src/question_mark.rs | 10 +++++----- clippy_lints/src/ranges.rs | 10 ++++------ clippy_lints/src/replace_box.rs | 4 ++-- clippy_lints/src/significant_drop_tightening.rs | 7 ++++--- clippy_lints/src/slow_vector_initialization.rs | 6 ++---- clippy_lints/src/swap.rs | 7 ++++--- clippy_lints/src/tuple_array_conversions.rs | 5 +++-- .../src/unnecessary_struct_initialization.rs | 5 +++-- clippy_lints/src/unwrap.rs | 8 ++++---- clippy_lints/src/useless_conversion.rs | 8 +++----- clippy_utils/src/lib.rs | 17 ++++------------- 40 files changed, 133 insertions(+), 129 deletions(-) diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index 52287be34c784..6f5c352c061c8 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -2,8 +2,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local, sym}; +use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -97,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { // 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. - && path_to_local(lhs).is_none_or(|lhs| local_is_initialized(cx, lhs)) + && lhs.res_local_id().is_none_or(|lhs| local_is_initialized(cx, lhs)) && let Some(resolved_impl) = cx.tcx.impl_of_assoc(resolved_fn.def_id()) // Derived forms don't implement `clone_from`/`clone_into`. // See https://github.com/rust-lang/rust/pull/98445#issuecomment-1190681305 diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index c88a0539d70e1..7bfe9201d812a 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::NumericLiteral; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{SpanRangeExt, snippet_opt}; use clippy_utils::visitors::{Visitable, for_each_expr_without_closures}; -use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local}; +use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias}; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -167,11 +168,11 @@ pub(super) fn check<'tcx>( sym::assert_ne_macro, sym::debug_assert_ne_macro, ]; - matches!(expr.span.ctxt().outer_expn_data().macro_def_id, Some(def_id) if + matches!(expr.span.ctxt().outer_expn_data().macro_def_id, Some(def_id) if cx.tcx.get_diagnostic_name(def_id).is_some_and(|sym| ALLOWED_MACROS.contains(&sym))) } - if let Some(id) = path_to_local(cast_expr) + if let Some(id) = cast_expr.res_local_id() && !cx.tcx.hir_span(id).eq_ctxt(cast_expr.span) { // Binding context is different than the identifiers context. diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 9ebb8e6e15d9e..de1362081323a 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop, peel_and_count_ty_refs}; use clippy_utils::{ DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, - path_to_local, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; @@ -239,7 +239,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { return; } - if let Some(local) = path_to_local(expr) { + if let Some(local) = expr.res_local_id() { self.check_local_usage(cx, expr, local); } diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 42b44778d58f5..13aefa83ae636 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -1,11 +1,9 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::VecArgs; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use clippy_utils::usage::{local_used_after_expr, local_used_in}; -use clippy_utils::{ - get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id, -}; +use clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local_id}; use rustc_abi::ExternAbi; use rustc_errors::Applicability; use rustc_hir::attrs::AttributeKind; @@ -218,7 +216,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx "redundant closure", |diag| { if let Some(mut snippet) = snippet_opt(cx, callee.span) { - if path_to_local(callee).is_some_and(|l| { + if callee.res_local_id().is_some_and(|l| { // FIXME: Do we really need this `local_used_in` check? // Isn't it checking something like... `callee(callee)`? // If somehow this check is needed, add some test for it, diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index 416aea51ea195..4dde968659e03 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, is_format_macro, root_macro_call_first_node}; -use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators, sym}; +use clippy_utils::res::MaybeResPath; +use clippy_utils::{get_parent_as_impl, is_diag_trait_item, peel_ref_operators, sym}; use rustc_ast::{FormatArgsPiece, FormatTrait}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath}; @@ -210,7 +211,7 @@ impl FormatImplExpr<'_, '_> { // Since the argument to fmt is itself a reference: &self let reference = peel_ref_operators(self.cx, arg); // Is the reference self? - if path_to_local(reference).map(|x| self.cx.tcx.hir_name(x)) == Some(kw::SelfLower) { + if reference.res_local_id().map(|x| self.cx.tcx.hir_name(x)) == Some(kw::SelfLower) { let FormatTraitNames { name, .. } = self.format_trait_impl; span_lint( self.cx, diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index 72f8795304055..c6b0e7c54c06f 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -1,12 +1,13 @@ +use clippy_utils::res::MaybeResPath; use rustc_hir::{self as hir, HirId, HirIdSet, intravisit}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::def_id::LocalDefId; use clippy_utils::diagnostics::span_lint; +use clippy_utils::iter_input_pats; use clippy_utils::ty::is_unsafe_fn; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{iter_input_pats, path_to_local}; use core::ops::ControlFlow; @@ -87,7 +88,7 @@ fn raw_ptr_arg(cx: &LateContext<'_>, arg: &hir::Param<'_>) -> Option { } fn check_arg(cx: &LateContext<'_>, raw_ptrs: &HirIdSet, arg: &hir::Expr<'_>) { - if path_to_local(arg).is_some_and(|id| raw_ptrs.contains(&id)) { + if arg.res_local_id().is_some_and(|id| raw_ptrs.contains(&id)) { span_lint( cx, NOT_UNSAFE_PTR_ARG_DEREF, diff --git a/clippy_lints/src/ifs/branches_sharing_code.rs b/clippy_lints/src/ifs/branches_sharing_code.rs index eb1025f71498e..b3f597cc8736e 100644 --- a/clippy_lints/src/ifs/branches_sharing_code.rs +++ b/clippy_lints/src/ifs/branches_sharing_code.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{ ContainsName, HirEqInterExpr, SpanlessEq, capture_local_usage, get_enclosing_block, hash_expr, hash_stmt, - path_to_local, }; use core::iter; use core::ops::ControlFlow; @@ -149,7 +149,7 @@ fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool { /// Checks if the statement modifies or moves any of the given locals. fn modifies_any_local<'tcx>(cx: &LateContext<'tcx>, s: &'tcx Stmt<'_>, locals: &HirIdSet) -> bool { for_each_expr_without_closures(s, |e| { - if let Some(id) = path_to_local(e) + if let Some(id) = e.res_local_id() && locals.contains(&id) && !capture_local_usage(cx, e).is_imm_ref() { @@ -198,7 +198,7 @@ fn scan_block_for_eq<'tcx>( let mut cond_locals = HirIdSet::default(); for &cond in conds { let _: Option = for_each_expr_without_closures(cond, |e| { - if let Some(id) = path_to_local(e) { + if let Some(id) = e.res_local_id() { cond_locals.insert(id); } ControlFlow::Continue(()) diff --git a/clippy_lints/src/ifs/ifs_same_cond.rs b/clippy_lints/src/ifs/ifs_same_cond.rs index ca76fc2587db9..3ea941320a086 100644 --- a/clippy_lints/src/ifs/ifs_same_cond.rs +++ b/clippy_lints/src/ifs/ifs_same_cond.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::res::MaybeResPath; use clippy_utils::ty::InteriorMut; -use clippy_utils::{SpanlessEq, eq_expr_value, find_binding_init, hash_expr, path_to_local, search_same}; +use clippy_utils::{SpanlessEq, eq_expr_value, find_binding_init, hash_expr, search_same}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -16,7 +17,7 @@ fn method_caller_is_mutable<'tcx>( interior_mut.is_interior_mut_ty(cx, caller_ty) || caller_ty.is_mutable_ptr() // `find_binding_init` will return the binding iff its not mutable - || path_to_local(caller_expr) + || caller_expr.res_local_id() .and_then(|hid| find_binding_init(cx, hid)) .is_none() } diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 8f9f71a147694..919702c5714ad 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -2,9 +2,10 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; +use clippy_utils::is_lint_allowed; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::ty::is_copy; -use clippy_utils::{is_lint_allowed, path_to_local}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -225,7 +226,7 @@ impl<'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'_, 'tcx> { } fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { - if let Some(local_id) = path_to_local(expr) { + if let Some(local_id) = expr.res_local_id() { let Self { cx, ref mut slice_lint_info, diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index d9c4b526da99e..a2da43c2ca245 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -1,10 +1,11 @@ use super::{IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeResPath; 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 clippy_utils::{get_enclosing_block, higher, sugg}; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir::intravisit::walk_block; @@ -67,7 +68,7 @@ pub(super) fn check<'tcx>( && !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) + && base_left.res_local_id() != base_right.res_local_id() { Some(( ty, @@ -128,7 +129,7 @@ fn build_manual_memcpy_suggestion<'tcx>( let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| { if let ExprKind::MethodCall(method, recv, [], _) = end.kind && method.ident.name == sym::len - && path_to_local(recv) == path_to_local(base) + && recv.res_local_id() == base.res_local_id() { if sugg.to_string() == end_str { sugg::EMPTY.into() @@ -364,7 +365,7 @@ fn get_details_from_idx<'tcx>( starts: &[Start<'tcx>], ) -> Option<(StartKind<'tcx>, Offset)> { fn get_start<'tcx>(e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option> { - let id = path_to_local(e)?; + let id = e.res_local_id()?; starts.iter().find(|start| start.id == id).map(|start| start.kind) } @@ -425,7 +426,7 @@ fn get_assignments<'a, 'tcx>( .chain(*expr) .filter(move |e| { if let ExprKind::AssignOp(_, place, _) = e.kind { - path_to_local(place).is_some_and(|id| { + place.res_local_id().is_some_and(|id| { !loop_counters .iter() // skip the first item which should be `StartKind::Range` diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 70ca452013f91..daeda227f670c 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -1,6 +1,7 @@ use super::MUT_RANGE_BOUND; use clippy_utils::diagnostics::span_lint_and_note; -use clippy_utils::{get_enclosing_block, higher, path_to_local}; +use clippy_utils::res::MaybeResPath; +use clippy_utils::{get_enclosing_block, higher}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Node, PatKind}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; @@ -39,7 +40,7 @@ fn mut_warn_with_span(cx: &LateContext<'_>, span: Option) { } fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option { - if let Some(hir_id) = path_to_local(bound) + if let Some(hir_id) = bound.res_local_id() && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) && let PatKind::Binding(BindingMode::MUT, ..) = pat.kind { diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index 097b84de49255..4135c63d4d7ff 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -1,10 +1,10 @@ use super::SAME_ITEM_PUSH; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::implements_trait; -use clippy_utils::{msrvs, path_to_local, std_or_core, sym}; +use clippy_utils::{msrvs, std_or_core, sym}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -126,7 +126,7 @@ impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> { if !self.non_deterministic_expr && !self.multiple_pushes && let Some((vec, _, _)) = self.vec_push - && let Some(hir_id) = path_to_local(vec) + && let Some(hir_id) = vec.res_local_id() { !self.used_locals.contains(&hir_id) } else { @@ -142,7 +142,7 @@ impl<'tcx> Visitor<'tcx> for SameItemPushVisitor<'_, 'tcx> { ExprKind::Loop(..) | ExprKind::Match(..) | ExprKind::If(..) => self.non_deterministic_expr = true, ExprKind::Block(block, _) => self.visit_block(block), _ => { - if let Some(hir_id) = path_to_local(expr) { + if let Some(hir_id) = expr.res_local_id() { self.used_locals.insert(hir_id); } walk_expr(self, expr); diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 2f6950b4380c3..842f542d22e7d 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -1,5 +1,6 @@ +use clippy_utils::res::MaybeResPath; 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 clippy_utils::{get_parent_expr, is_integer_const, path_to_local_id, sugg}; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr, walk_local}; @@ -47,7 +48,7 @@ impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { impl<'tcx> Visitor<'tcx> for IncrementVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { // If node is a variable - if let Some(def_id) = path_to_local(expr) { + if let Some(def_id) = expr.res_local_id() { if let Some(parent) = get_parent_expr(self.cx, expr) { let state = self.states.entry(def_id).or_insert(IncrementVisitorVarState::Initial); if *state == IncrementVisitorVarState::IncrOnce { diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs index 60782f445ab91..a81c4dc6a7931 100644 --- a/clippy_lints/src/manual_float_methods.rs +++ b/clippy_lints/src/manual_float_methods.rs @@ -1,9 +1,10 @@ use clippy_config::Conf; use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{is_from_proc_macro, path_to_local}; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -138,7 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { // Checking all possible scenarios using a function would be a hopeless task, as we have // 16 possible alignments of constants/operands. For now, let's use `partition`. && let mut exprs = [lhs_lhs, lhs_rhs, rhs_lhs, rhs_rhs] - && exprs.iter_mut().partition_in_place(|i| path_to_local(i).is_some()) == 2 + && exprs.iter_mut().partition_in_place(|i| i.res_local_id().is_some()) == 2 && !expr.span.in_external_macro(cx.sess().source_map()) && ( is_not_const(cx.tcx, cx.tcx.hir_enclosing_body_owner(expr.hir_id).into()) @@ -149,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { && let ctxt = expr.span.ctxt() && let Some(const_1) = ecx.eval_local(const_1, ctxt) && let Some(const_2) = ecx.eval_local(const_2, ctxt) - && path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s)) + && first.res_local_id().is_some_and(|f| second.res_local_id().is_some_and(|s| f == s)) // The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in // case somebody does that for some reason && (const_1.is_pos_infinity() && const_2.is_neg_infinity() diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 2eebb2430fd94..8c6abbeac4489 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -2,8 +2,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::sugg::Sugg; -use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operators, sym}; +use clippy_utils::{higher, is_in_const_context, peel_ref_operators, sym}; use rustc_ast::LitKind::{Byte, Char}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; @@ -125,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { } fn get_ty_sugg<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'_>) -> Option<(Span, Ty<'tcx>)> { - let local_hid = path_to_local(arg)?; + let local_hid = arg.res_local_id()?; if let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid) // `ty_span` and `span` are the same for inferred type, thus a type suggestion must be given && ty_span == span diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 1e91a429fe45c..d993ed48eac4a 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -1,9 +1,10 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_in_const_context; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_context; -use clippy_utils::{is_in_const_context, path_to_local}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -64,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { && let ExprKind::Binary(rem2_op, rem2_lhs, rem2_rhs) = add_other.kind && rem2_op.node == BinOpKind::Rem && const1 == const2 - && let Some(hir_id) = path_to_local(rem2_lhs) + && let Some(hir_id) = rem2_lhs.res_local_id() && let Some(const3) = check_for_unsigned_int_constant(cx, ctxt, rem2_rhs) // Also ensures the const is nonzero since zero can't be a divisor && const2 == const3 diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 347d4f9fa4b5b..79f737f07eb1e 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -1,12 +1,10 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::msrvs::Msrv; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; -use clippy_utils::{ - SpanlessEq, get_ref_operators, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, -}; +use clippy_utils::{SpanlessEq, get_ref_operators, is_unit_expr, peel_blocks_with_stmt, peel_ref_operators}; use rustc_ast::BorrowKind; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; @@ -67,7 +65,7 @@ fn check_arm<'tcx>( && outer_pat.span.eq_ctxt(inner_scrutinee.span) // match expression must be a local binding // match { .. } - && let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee)) + && let Some(binding_id) = peel_ref_operators(cx, inner_scrutinee).res_local_id() && !pat_contains_disallowed_or(cx, inner_then_pat, msrv) // the binding must come from the pattern of the containing match arm // .... => match { .. } diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index 818e504245549..be914429edb44 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{SpanlessEq, fulfill_or_allowed, hash_expr, is_lint_allowed, path_to_local, search_same}; +use clippy_utils::{SpanlessEq, fulfill_or_allowed, hash_expr, is_lint_allowed, search_same}; use core::cmp::Ordering; use core::{iter, slice}; use itertools::Itertools; @@ -61,8 +62,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { let check_eq_with_pat = |expr_a: &Expr<'_>, expr_b: &Expr<'_>| { let mut local_map: HirIdMap = HirIdMap::default(); let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { - if let Some(a_id) = path_to_local(a) - && let Some(b_id) = path_to_local(b) + if let Some(a_id) = a.res_local_id() + && let Some(b_id) = b.res_local_id() && let entry = match local_map.entry(a_id) { HirIdMapEntry::Vacant(entry) => entry, // check if using the same bindings as before diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs index 7c6d45e424006..d39e315cae1f2 100644 --- a/clippy_lints/src/matches/redundant_guards.rs +++ b/clippy_lints/src/matches/redundant_guards.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::msrvs::Msrv; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::visitors::{for_each_expr_without_closures, is_local_used}; -use clippy_utils::{is_in_const_context, path_to_local, sym}; +use clippy_utils::{is_in_const_context, sym}; use rustc_ast::{BorrowKind, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -164,7 +165,7 @@ fn get_pat_binding<'tcx>( guard_expr: &Expr<'_>, outer_arm: &Arm<'tcx>, ) -> Option { - if let Some(local) = path_to_local(guard_expr) + if let Some(local) = guard_expr.res_local_id() && !is_local_used(cx, outer_arm.body, local) { let mut span = None; diff --git a/clippy_lints/src/methods/is_empty.rs b/clippy_lints/src/methods/is_empty.rs index 545bef1a4c5bc..add01b6a08376 100644 --- a/clippy_lints/src/methods/is_empty.rs +++ b/clippy_lints/src/methods/is_empty.rs @@ -1,7 +1,8 @@ use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::{is_assert_macro, root_macro_call}; -use clippy_utils::{find_binding_init, get_parent_expr, is_inside_always_const_context, path_to_local}; +use clippy_utils::res::MaybeResPath; +use clippy_utils::{find_binding_init, get_parent_expr, is_inside_always_const_context}; use rustc_hir::{Expr, HirId}; use rustc_lint::{LateContext, LintContext}; use rustc_span::sym; @@ -45,7 +46,8 @@ fn is_under_cfg(cx: &LateContext<'_>, id: HirId) -> bool { /// 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) + while let Some(init) = expr + .res_local_id() .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)) diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs index fedb7c22eded6..cf88cab4a77ef 100644 --- a/clippy_lints/src/methods/iter_skip_next.rs +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_trait_method; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; -use clippy_utils::{is_trait_method, path_to_local}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{BindingMode, Node, PatKind}; @@ -19,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr expr.span.trim_start(recv.span).unwrap(), "called `skip(..).next()` on an iterator", |diag| { - if let Some(id) = path_to_local(recv) + if let Some(id) = recv.res_local_id() && let Node::Pat(pat) = cx.tcx.hir_node(id) && let PatKind::Binding(ann, _, _, _) = pat.kind && ann != BindingMode::MUT diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 34bae0132c97d..2997167f4a58a 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -2,13 +2,13 @@ use std::ops::ControlFlow; use super::NEEDLESS_COLLECT; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{has_non_owning_mutable_access, make_normalized_projection, make_projection}; use clippy_utils::{ - CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local, - path_to_local_id, sym, + CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local_id, + sym, }; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; @@ -383,7 +383,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { return; } - if let Some(hir_id) = path_to_local(recv) + if let Some(hir_id) = recv.res_local_id() && let Some(index) = self.hir_id_uses_map.remove(&hir_id) { if self @@ -554,7 +554,7 @@ impl<'tcx> Visitor<'tcx> for IteratorMethodCheckVisitor<'_, 'tcx> { return ControlFlow::Break(()); } else if let ExprKind::Assign(place, value, _span) = &expr.kind && value.hir_id == self.hir_id_of_expr - && let Some(id) = path_to_local(place) + && let Some(id) = place.res_local_id() { // our iterator was directly assigned to a variable self.hir_id_of_let_binding = Some(id); diff --git a/clippy_lints/src/methods/string_lit_chars_any.rs b/clippy_lints/src/methods/string_lit_chars_any.rs index f0f9d30d3000a..660ecbc5e6ceb 100644 --- a/clippy_lints/src/methods/string_lit_chars_any.rs +++ b/clippy_lints/src/methods/string_lit_chars_any.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{is_from_proc_macro, is_trait_method, path_to_local}; +use clippy_utils::{is_from_proc_macro, is_trait_method}; use itertools::Itertools; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -25,8 +26,8 @@ pub(super) fn check<'tcx>( && let LitKind::Str(val, _) = lit_kind.node && let ExprKind::Binary(kind, lhs, rhs) = body.kind && let BinOpKind::Eq = kind.node - && let Some(lhs_path) = path_to_local(lhs) - && let Some(rhs_path) = path_to_local(rhs) + && let Some(lhs_path) = lhs.res_local_id() + && let Some(rhs_path) = rhs.res_local_id() && let scrutinee = match (lhs_path == arg, rhs_path == arg) { (true, false) => rhs, (false, true) => lhs, diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 20cf35363d13f..4142f9f75773e 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -1,10 +1,11 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{get_iterator_item_ty, implements_trait}; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local}; +use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr}; use core::ops::ControlFlow; use itertools::Itertools; use rustc_errors::Applicability; @@ -50,7 +51,7 @@ pub fn check_for_loop_iter( // check whether `expr` is mutable fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(hir_id) = path_to_local(expr) + if let Some(hir_id) = expr.res_local_id() && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) { matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)) diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index 3b44d4b60d320..6ed2628c37e20 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id, sym}; +use clippy_utils::res::MaybeResPath; +use clippy_utils::{get_parent_expr, path_to_local_id, sym}; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, LetStmt, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -84,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Find a write to a local variable. let var = if let ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) = expr.kind - && let Some(var) = path_to_local(lhs) + && let Some(var) = lhs.res_local_id() && expr.span.desugaring_kind().is_none() { var diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index a914267cf5002..464a91959a8eb 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::path_to_local; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{SourceText, SpanRangeExt, snippet}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures, is_local_used}; @@ -116,7 +116,7 @@ impl LocalAssign { } Some(Self { - lhs_id: path_to_local(lhs)?, + lhs_id: lhs.res_local_id()?, rhs_span: rhs.span.source_callsite(), span, }) diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 0d6666eed455c..701923cf6efc4 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{expr_type_is_certain, has_drop}; -use clippy_utils::{ - in_automatically_derived, is_inside_always_const_context, is_lint_allowed, path_to_local, peel_blocks, -}; +use clippy_utils::{in_automatically_derived, is_inside_always_const_context, is_lint_allowed, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ @@ -109,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { } fn check_expr(&mut self, _: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let Some(def_id) = path_to_local(expr) { + if let Some(def_id) = expr.res_local_id() { self.underscore_bindings.swap_remove(&def_id); } } diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index 10cc4f48456fd..63d9271ecf058 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::res::MaybeQPath; -use clippy_utils::{get_expr_use_or_unification_node, path_to_local, path_to_local_id}; +use clippy_utils::res::{MaybeQPath, MaybeResPath}; +use clippy_utils::{get_expr_use_or_unification_node, path_to_local_id}; use core::cell::Cell; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; @@ -359,7 +359,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { } fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) { - if let Some(id) = path_to_local(e) + if let Some(id) = e.res_local_id() && let Some(param) = self.params.get_by_id_mut(id) { let typeck = cx.typeck_results(); diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index ec82817ef2bdb..71c1a40ca8132 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -3,7 +3,7 @@ use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::SpanRangeExt; use clippy_utils::sugg::Sugg; use clippy_utils::visitors::contains_unsafe_block; -use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_to_local, std_or_core, sym}; +use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, std_or_core, sym}; use hir::LifetimeKind; use rustc_abi::ExternAbi; use rustc_errors::{Applicability, MultiSpan}; @@ -562,7 +562,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[ } // Check if this is local we care about - let Some(&args_idx) = path_to_local(e).and_then(|id| self.bindings.get(&id)) else { + let Some(&args_idx) = e.res_local_id().and_then(|id| self.bindings.get(&id)) else { return walk_expr(self, e); }; let args = &self.args[args_idx]; diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index d0cce2b6d8751..ce8db486de1af 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -4,15 +4,15 @@ use clippy_config::Conf; use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{ eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, - pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, - span_contains_cfg, span_contains_comment, sym, + pat_and_expr_can_be_question_mark, path_to_local_id, peel_blocks, peel_blocks_with_stmt, span_contains_cfg, + span_contains_comment, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; @@ -252,7 +252,7 @@ fn expr_return_none_or_err( .qpath_res(qpath, expr.hir_id) .ctor_parent(cx) .is_lang_item(cx, OptionNone), - sym::Result => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr), + sym::Result => expr.res_local_id().is_some() && expr.res_local_id() == cond_expr.res_local_id(), _ => false, }, ExprKind::Call(call_expr, [arg]) => { @@ -492,7 +492,7 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: .is_none() { if !is_copy(cx, caller_ty) - && let Some(hir_id) = path_to_local(let_expr) + && let Some(hir_id) = let_expr.res_local_id() && local_used_after_expr(cx, hir_id, expr) { return; diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 945b67f821f6e..e4c91b7efd2bd 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -2,13 +2,11 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeQPath; +use clippy_utils::res::{MaybeQPath, MaybeResPath}; use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; -use clippy_utils::{ - expr_use_ctxt, fn_def_id, get_parent_expr, higher, is_in_const_context, is_integer_const, path_to_local, -}; +use clippy_utils::{expr_use_ctxt, fn_def_id, get_parent_expr, higher, is_in_const_context, is_integer_const}; use rustc_ast::Mutability; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; @@ -321,7 +319,7 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option (true, Ordering::Less), _ => return None, }; - if let Some(id) = path_to_local(l) { + if let Some(id) = l.res_local_id() { if let Some(c) = ConstEvalCtxt::new(cx).eval(r) { return Some(RangeBounds { val: c, @@ -333,7 +331,7 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option for ReplaceBox { && !rhs.span.from_expansion() && let lhs_ty = cx.typeck_results().expr_ty(lhs) // No diagnostic for late-initialized locals - && path_to_local(lhs).is_none_or(|local| local_is_initialized(cx, local)) + && lhs.res_local_id().is_none_or(|local| local_is_initialized(cx, local)) && let Some(inner_ty) = lhs_ty.boxed_ty() { if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index 9110f684bd10c..dcce906499585 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{indent_of, snippet}; -use clippy_utils::{expr_or_init, get_attr, path_to_local, peel_hir_expr_unary, sym}; +use clippy_utils::{expr_or_init, get_attr, peel_hir_expr_unary, sym}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -276,7 +277,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { && let hir::PatKind::Binding(_, hir_id, ident, _) = local.pat.kind && !self.ap.apas.contains_key(&hir_id) && { - if let Some(local_hir_id) = path_to_local(expr) { + if let Some(local_hir_id) = expr.res_local_id() { local_hir_id == hir_id } else { true @@ -301,7 +302,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { modify_apa_params(&mut apa); let _ = self.ap.apas.insert(hir_id, apa); } else { - let Some(hir_id) = path_to_local(expr) else { + let Some(hir_id) = expr.res_local_id() else { return; }; let Some(apa) = self.ap.apas.get_mut(&hir_id) else { diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 237c8e66ee3fc..ea8c56d9a79b6 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -2,9 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::sugg::Sugg; -use clippy_utils::{ - SpanlessEq, get_enclosing_block, is_integer_literal, path_to_local, path_to_local_id, span_contains_comment, sym, -}; +use clippy_utils::{SpanlessEq, get_enclosing_block, is_integer_literal, path_to_local_id, span_contains_comment, sym}; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind}; @@ -102,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Matches initialization on reassignments. For example: `vec = Vec::with_capacity(100)` if let ExprKind::Assign(left, right, _) = expr.kind - && let Some(local_id) = path_to_local(left) + && let Some(local_id) = left.res_local_id() && let Some(size_expr) = Self::as_vec_initializer(cx, right) { let vi = VecAllocation { diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 319051dce685d..c3cb2c09752fe 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet_indent, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, path_to_local, std_or_core}; +use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, std_or_core}; use itertools::Itertools; use rustc_data_structures::fx::FxIndexSet; @@ -361,7 +361,8 @@ impl<'tcx> IndexBinding<'_, 'tcx> { // - Variable declaration is outside the suggestion span // - Variable is not used as an index or elsewhere later if !self.suggest_span.contains(init.span) - || path_to_local(expr) + || expr + .res_local_id() .is_some_and(|hir_id| !self.suggest_span.contains(self.cx.tcx.hir_span(hir_id))) || !self.is_used_other_than_swapping(first_segment.ident) { diff --git a/clippy_lints/src/tuple_array_conversions.rs b/clippy_lints/src/tuple_array_conversions.rs index 3c21d194b81d5..5d0945bece553 100644 --- a/clippy_lints/src/tuple_array_conversions.rs +++ b/clippy_lints/src/tuple_array_conversions.rs @@ -1,8 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::visitors::for_each_local_use_after_expr; -use clippy_utils::{is_from_proc_macro, path_to_local}; use itertools::Itertools; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind, Node, PatKind}; @@ -152,7 +153,7 @@ fn all_bindings_are_for_conv<'tcx>( locals: &[&Expr<'_>], kind: ToType, ) -> bool { - let Some(locals) = locals.iter().map(|e| path_to_local(e)).collect::>>() else { + let Some(locals) = locals.iter().map(|e| e.res_local_id()).collect::>>() else { return false; }; let local_parents = locals.iter().map(|l| cx.tcx.parent_hir_node(*l)).collect::>(); diff --git a/clippy_lints/src/unnecessary_struct_initialization.rs b/clippy_lints/src/unnecessary_struct_initialization.rs index 5792b6b3178d7..51596859e4c7b 100644 --- a/clippy_lints/src/unnecessary_struct_initialization.rs +++ b/clippy_lints/src/unnecessary_struct_initialization.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::ty::is_copy; -use clippy_utils::{get_parent_expr, is_mutable, path_to_local}; +use clippy_utils::{get_parent_expr, is_mutable}; use rustc_hir::{Expr, ExprField, ExprKind, Path, QPath, StructTailExpr, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -162,7 +163,7 @@ fn check_references(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) && let parent_ty = cx.typeck_results().expr_ty_adjusted(parent) && parent_ty.is_any_ptr() { - if is_copy(cx, cx.typeck_results().expr_ty(expr_a)) && path_to_local(expr_b).is_some() { + if is_copy(cx, cx.typeck_results().expr_ty(expr_a)) && expr_b.res_local_id().is_some() { // When the type implements `Copy`, a reference to the new struct works on the // copy. Using the original would borrow it. return false; diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 2f31aa55af6f8..99201a1ca215f 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,9 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::Msrv; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::usage::is_potentially_local_place; -use clippy_utils::{can_use_if_let_chains, higher, path_to_local, sym}; +use clippy_utils::{can_use_if_let_chains, higher, sym}; use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, UnOp}; @@ -169,7 +169,7 @@ fn collect_unwrap_info<'tcx>( }, ExprKind::Unary(UnOp::Not, expr) => collect_unwrap_info(cx, if_expr, expr, branch, !invert, false), ExprKind::MethodCall(method_name, receiver, [], _) - if let Some(local_id) = path_to_local(receiver) + if let Some(local_id) = receiver.res_local_id() && let ty = cx.typeck_results().expr_ty(receiver) && let name = method_name.ident.name && let Some((kind, unwrappable)) = option_or_result_call(cx, ty, name) => @@ -318,7 +318,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { // find `unwrap[_err]()` or `expect("...")` calls: if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind && let (self_arg, as_ref_kind) = consume_option_as_ref(self_arg) - && let Some(id) = path_to_local(self_arg) + && let Some(id) = self_arg.res_local_id() && matches!(method_name.ident.name, sym::unwrap | sym::expect | sym::unwrap_err) && let call_to_unwrap = matches!(method_name.ident.name, sym::unwrap | sym::expect) && let Some(unwrappable) = self.unwrappables.iter() diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index a99b5a9576a31..dab6a2351a234 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,11 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; use clippy_utils::ty::{is_copy, same_type_modulo_regions}; -use clippy_utils::{ - get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, sym, -}; +use clippy_utils::{get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, sym}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, PatKind}; @@ -309,7 +307,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } } - if let Some(id) = path_to_local(recv) + if let Some(id) = recv.res_local_id() && let Node::Pat(pat) = cx.tcx.hir_node(id) && let PatKind::Binding(ann, ..) = pat.kind && ann != BindingMode::MUT diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 7a215d3916221..9437a378c390e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -174,7 +174,8 @@ macro_rules! extract_msrv_attr { /// // ^^^ input /// ``` pub 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) + while let Some(init) = expr + .res_local_id() .and_then(|id| find_binding_init(cx, id)) .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty()) { @@ -429,20 +430,10 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator) -> Option { - if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind - && let Res::Local(id) = path.res - { - return Some(id); - } - None -} - /// Returns true if the expression is a path to a local with the specified `HirId`. /// Use this function to see if an expression matches a function argument or a match binding. pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool { - path_to_local(expr) == Some(id) + expr.res_local_id() == Some(id) } /// If the expression is a path to a local (with optional projections), @@ -3438,7 +3429,7 @@ pub fn expr_requires_coercion<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) - /// Returns `true` if `expr` designates a mutable static, a mutable local binding, or an expression /// that can be owned. pub fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(hir_id) = path_to_local(expr) + if let Some(hir_id) = expr.res_local_id() && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) { matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)) From a6078f87db8922aa23b9882ea4ace8a88f7ca6a9 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 16:48:41 -0400 Subject: [PATCH 58/89] Remove `path_to_local_id` --- clippy_lints/src/collection_is_never_read.rs | 10 +++++----- clippy_lints/src/eta_reduction.rs | 4 ++-- clippy_lints/src/let_if_seq.rs | 4 ++-- clippy_lints/src/lines_filter_map_ok.rs | 6 +++--- .../src/loops/char_indices_as_byte_indices.rs | 8 ++++---- clippy_lints/src/loops/manual_flatten.rs | 5 +++-- clippy_lints/src/loops/utils.rs | 4 ++-- clippy_lints/src/manual_clamp.rs | 9 ++++----- clippy_lints/src/manual_hash_one.rs | 5 +++-- .../matches/infallible_destructuring_match.rs | 5 +++-- clippy_lints/src/matches/manual_filter.rs | 5 ++--- clippy_lints/src/matches/manual_utils.rs | 10 +++++----- clippy_lints/src/methods/bytecount.rs | 6 +++--- clippy_lints/src/methods/filter_map.rs | 18 +++++++++--------- clippy_lints/src/methods/manual_inspect.rs | 9 +++++---- clippy_lints/src/methods/manual_ok_or.rs | 5 ++--- .../methods/needless_character_iteration.rs | 8 ++++---- clippy_lints/src/methods/needless_collect.rs | 11 +++++------ .../src/methods/option_as_ref_deref.rs | 8 ++++---- clippy_lints/src/methods/str_splitn.rs | 5 +++-- .../src/methods/unnecessary_filter_map.rs | 8 ++++---- clippy_lints/src/methods/unnecessary_fold.rs | 7 ++++--- clippy_lints/src/methods/unnecessary_map_or.rs | 10 +++++----- clippy_lints/src/methods/useless_asref.rs | 7 ++++--- clippy_lints/src/methods/utils.rs | 6 +++--- .../src/mixed_read_write_in_expression.rs | 4 ++-- clippy_lints/src/needless_pass_by_value.rs | 6 +++--- clippy_lints/src/only_used_in_recursion.rs | 4 ++-- clippy_lints/src/pathbuf_init_then_push.rs | 6 +++--- clippy_lints/src/question_mark.rs | 14 ++++++++------ .../src/reserve_after_initialization.rs | 5 +++-- clippy_lints/src/returns/let_and_return.rs | 5 +++-- clippy_lints/src/shadow.rs | 4 ++-- clippy_lints/src/slow_vector_initialization.rs | 6 +++--- clippy_lints/src/string_patterns.rs | 9 +++++---- clippy_lints/src/uninit_vec.rs | 6 +++--- clippy_lints/src/unused_peekable.rs | 6 +++--- clippy_lints/src/vec_init_then_push.rs | 5 +++-- clippy_lints/src/zombie_processes.rs | 6 +++--- clippy_utils/src/lib.rs | 10 ++-------- clippy_utils/src/usage.rs | 5 +++-- clippy_utils/src/visitors.rs | 11 ++++++----- 42 files changed, 150 insertions(+), 145 deletions(-) diff --git a/clippy_lints/src/collection_is_never_read.rs b/clippy_lints/src/collection_is_never_read.rs index 95f3589c4477f..fd84ce70bd717 100644 --- a/clippy_lints/src/collection_is_never_read.rs +++ b/clippy_lints/src/collection_is_never_read.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::res::MaybeDef; +use clippy_utils::get_enclosing_block; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::visitors::{Visitable, for_each_expr}; -use clippy_utils::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -81,7 +81,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI // Inspect all expressions and sub-expressions in the block. for_each_expr(cx, block, |expr| { // Ignore expressions that are not simply `id`. - if !path_to_local_id(expr, id) { + if expr.res_local_id() != Some(id) { return ControlFlow::Continue(()); } @@ -93,7 +93,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI // id = ...; // Not reading `id`. if let Node::Expr(parent) = cx.tcx.parent_hir_node(expr.hir_id) && let ExprKind::Assign(lhs, ..) = parent.kind - && path_to_local_id(lhs, id) + && lhs.res_local_id() == Some(id) { return ControlFlow::Continue(()); } @@ -107,7 +107,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI // have side effects, so consider them a read. if let Node::Expr(parent) = cx.tcx.parent_hir_node(expr.hir_id) && let ExprKind::MethodCall(_, receiver, args, _) = parent.kind - && path_to_local_id(receiver, id) + && receiver.res_local_id() == Some(id) && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id) && !method_def_id.is_local() { diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 13aefa83ae636..21385ee4fdc7f 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -3,7 +3,7 @@ use clippy_utils::higher::VecArgs; use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use clippy_utils::usage::{local_used_after_expr, local_used_in}; -use clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local_id}; +use clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate}; use rustc_abi::ExternAbi; use rustc_errors::Applicability; use rustc_hir::attrs::AttributeKind; @@ -305,7 +305,7 @@ fn check_inputs( matches!( p.pat.kind, PatKind::Binding(BindingMode::NONE, id, _, None) - if path_to_local_id(arg, id) + if arg.res_local_id() == Some(id) ) // Only allow adjustments which change regions (i.e. re-borrowing). && typeck diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index e480c8fbed53c..2dbf55a8540ba 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::path_to_local_id; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; use rustc_errors::Applicability; @@ -145,7 +145,7 @@ fn check_assign<'tcx>( && let Some(expr) = block.stmts.iter().last() && let hir::StmtKind::Semi(expr) = expr.kind && let hir::ExprKind::Assign(var, value, _) = expr.kind - && path_to_local_id(var, decl) + && var.res_local_id() == Some(decl) { if block .stmts diff --git a/clippy_lints/src/lines_filter_map_ok.rs b/clippy_lints/src/lines_filter_map_ok.rs index ab00cd9ca539c..dacc2c1bdbdac 100644 --- a/clippy_lints/src/lines_filter_map_ok.rs +++ b/clippy_lints/src/lines_filter_map_ok.rs @@ -1,8 +1,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeDef; -use clippy_utils::{is_diag_item_method, is_trait_method, path_to_local_id, sym}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::{is_diag_item_method, is_trait_method, sym}; use rustc_errors::Applicability; use rustc_hir::{Body, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -120,7 +120,7 @@ fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_name: Symbol) -> params: [param], value, .. } = cx.tcx.hir_body(*body) && let ExprKind::MethodCall(method, receiver, [], _) = value.kind - && path_to_local_id(receiver, param.pat.hir_id) + && receiver.res_local_id() == Some(param.pat.hir_id) && let Some(method_did) = cx.typeck_results().type_dependent_def_id(value.hir_id) { is_diag_item_method(cx, method_did, sym::Result) && method.ident.name == sym::ok diff --git a/clippy_lints/src/loops/char_indices_as_byte_indices.rs b/clippy_lints/src/loops/char_indices_as_byte_indices.rs index f2c87a2863c5f..7acf2a5462220 100644 --- a/clippy_lints/src/loops/char_indices_as_byte_indices.rs +++ b/clippy_lints/src/loops/char_indices_as_byte_indices.rs @@ -1,9 +1,9 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{eq_expr_value, higher, path_to_local_id, sym}; +use clippy_utils::{eq_expr_value, higher, sym}; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::{Expr, ExprKind, LangItem, Node, Pat, PatKind}; use rustc_lint::LateContext; @@ -49,7 +49,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, iterable: &Expr { // Destructured iterator element `(idx, _)`, look for uses of the binding for_each_expr(cx, body, |expr| { - if path_to_local_id(expr, binding_id) { + if expr.res_local_id() == Some(binding_id) { check_index_usage(cx, expr, pat, enumerate_span, chars_span, chars_recv); } CONTINUE @@ -58,7 +58,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, iterable: &Expr // Bound as a tuple, look for `tup.0` for_each_expr(cx, body, |expr| { if let ExprKind::Field(e, field) = expr.kind - && path_to_local_id(e, binding_id) + && e.res_local_id() == Some(binding_id) && field.name == sym::integer(0) { check_index_usage(cx, expr, pat, enumerate_span, chars_span, chars_recv); diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index ddb8bb536c04e..96de118b5233b 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -2,9 +2,10 @@ use super::MANUAL_FLATTEN; use super::utils::make_iterator_snippet; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{HasSession, indent_of, reindent_multiline, snippet_with_applicability}; use clippy_utils::visitors::is_local_used; -use clippy_utils::{higher, is_refutable, path_to_local_id, peel_blocks_with_stmt, span_contains_comment}; +use clippy_utils::{higher, is_refutable, peel_blocks_with_stmt, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, Pat, PatKind}; @@ -27,7 +28,7 @@ pub(super) fn check<'tcx>( = higher::IfLet::hir(cx, inner_expr) // Ensure match_expr in `if let` statement is the same as the pat from the for-loop && let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind - && path_to_local_id(let_expr, pat_hir_id) + && let_expr.res_local_id() == Some(pat_hir_id) // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` && let PatKind::TupleStruct(ref qpath, [inner_pat], _) = let_pat.kind && let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, let_pat.hir_id) diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 842f542d22e7d..7bf7565f73c71 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -1,6 +1,6 @@ use clippy_utils::res::MaybeResPath; use clippy_utils::ty::{has_iter_method, implements_trait}; -use clippy_utils::{get_parent_expr, is_integer_const, path_to_local_id, sugg}; +use clippy_utils::{get_parent_expr, is_integer_const, sugg}; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr, walk_local}; @@ -176,7 +176,7 @@ impl<'tcx> Visitor<'tcx> for InitializeVisitor<'_, 'tcx> { } // If node is the desired variable, see how it's used - if path_to_local_id(expr, self.var_id) { + if expr.res_local_id() == Some(self.var_id) { if self.past_loop { self.state = InitializeVisitorState::DontWarn; return; diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index eaaab292f2f53..f7cc23517bcfa 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -8,8 +8,7 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::is_const_evaluatable; use clippy_utils::{ - eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_to_local_id, peel_blocks, - peel_blocks_with_stmt, sym, + eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, peel_blocks, peel_blocks_with_stmt, sym, }; use itertools::Itertools; use rustc_errors::{Applicability, Diag}; @@ -436,7 +435,7 @@ fn is_match_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opt let first = BinaryOp::new(first)?; let second = BinaryOp::new(second)?; if let PatKind::Binding(_, binding, _, None) = &last_arm.pat.kind - && path_to_local_id(peel_blocks_with_stmt(last_arm.body), *binding) + && peel_blocks_with_stmt(last_arm.body).res_local_id() == Some(*binding) && last_arm.guard.is_none() { // Proceed as normal @@ -656,8 +655,8 @@ fn is_clamp_meta_pattern<'tcx>( let (min, max) = (second_expr, first_expr); let refers_to_input = match input_hir_ids { Some((first_hir_id, second_hir_id)) => { - path_to_local_id(peel_blocks(first_bin.left), first_hir_id) - && path_to_local_id(peel_blocks(second_bin.left), second_hir_id) + peel_blocks(first_bin.left).res_local_id() == Some(first_hir_id) + && peel_blocks(second_bin.left).res_local_id() == Some(second_hir_id) }, None => eq_expr_value(cx, first_bin.left, second_bin.left), }; diff --git a/clippy_lints/src/manual_hash_one.rs b/clippy_lints/src/manual_hash_one.rs index b3ee45cc02098..5c1289231eece 100644 --- a/clippy_lints/src/manual_hash_one.rs +++ b/clippy_lints/src/manual_hash_one.rs @@ -1,9 +1,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; use clippy_utils::visitors::{is_local_used, local_used_once}; -use clippy_utils::{is_trait_method, path_to_local_id, sym}; +use clippy_utils::{is_trait_method, sym}; use rustc_errors::Applicability; use rustc_hir::{BindingMode, ExprKind, LetStmt, Node, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -82,7 +83,7 @@ impl LateLintPass<'_> for ManualHashOne { && let ExprKind::MethodCall(seg, hashed_value, [ref_to_hasher], _) = hash_expr.kind && seg.ident.name == sym::hash && is_trait_method(cx, hash_expr, sym::Hash) - && path_to_local_id(ref_to_hasher.peel_borrows(), hasher) + && ref_to_hasher.peel_borrows().res_local_id() == Some(hasher) && let maybe_finish_stmt = stmts.next() // There should be no more statements referencing `hasher` diff --git a/clippy_lints/src/matches/infallible_destructuring_match.rs b/clippy_lints/src/matches/infallible_destructuring_match.rs index 93d7683d2af81..be4b4f346dbcf 100644 --- a/clippy_lints/src/matches/infallible_destructuring_match.rs +++ b/clippy_lints/src/matches/infallible_destructuring_match.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs}; +use clippy_utils::{peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{ExprKind, LetStmt, MatchSource, PatKind, QPath}; use rustc_lint::LateContext; @@ -17,7 +18,7 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool { && args.len() == 1 && let PatKind::Binding(binding, arg, ..) = strip_pat_refs(&args[0]).kind && let body = peel_blocks(arms[0].body) - && path_to_local_id(body, arg) + && body.res_local_id() == Some(arg) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index 227db74b9d78a..d7224052ebc58 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::path_to_local_id; -use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::visitors::contains_unsafe_block; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -67,7 +66,7 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: { return ctxt == expr.span.ctxt() && callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) - && path_to_local_id(arg, target); + && arg.res_local_id() == Some(target); } false } diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index 60925db893dae..235cb9e4ecce8 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -1,12 +1,12 @@ use crate::map_unit_fn::OPTION_MAP_UNIT_FN; use crate::matches::MATCH_AS_REF; -use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_copy, is_unsafe_fn, peel_and_count_ty_refs}; use clippy_utils::{ - CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, path_to_local_id, - peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, + CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, peel_blocks, + peel_hir_expr_refs, peel_hir_expr_while, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; @@ -138,7 +138,7 @@ where { snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() } else { - if path_to_local_id(some_expr.expr, id) + if some_expr.expr.res_local_id() == Some(id) && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) && binding_ref.is_some() { @@ -190,7 +190,7 @@ pub struct SuggInfo<'a> { fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { match expr.kind { ExprKind::Call(func, [arg]) - if path_to_local_id(arg, binding) + if arg.res_local_id() == Some(binding) && cx.typeck_results().expr_adjustments(arg).is_empty() && !is_unsafe_fn(cx, cx.typeck_results().expr_ty(func).peel_refs()) => { diff --git a/clippy_lints/src/methods/bytecount.rs b/clippy_lints/src/methods/bytecount.rs index f3a34ce6964ed..77ac181ee633a 100644 --- a/clippy_lints/src/methods/bytecount.rs +++ b/clippy_lints/src/methods/bytecount.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::visitors::is_local_used; -use clippy_utils::{path_to_local_id, peel_blocks, peel_ref_operators, strip_pat_refs}; +use clippy_utils::{peel_blocks, peel_ref_operators, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>( .is_diag_item(cx, sym::SliceIter) && let operand_is_arg = (|expr| { let expr = peel_ref_operators(cx, peel_blocks(expr)); - path_to_local_id(expr, arg_id) + expr.res_local_id() == Some(arg_id) }) && let needle = if operand_is_arg(l) { r diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index d813105165cef..53ce09c16e441 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::{is_panic, matching_root_macro_call, root_macro_call}; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{indent_of, reindent_multiline, snippet}; -use clippy_utils::{SpanlessEq, higher, is_trait_method, path_to_local_id, peel_blocks, sym}; +use clippy_utils::{SpanlessEq, higher, is_trait_method, peel_blocks, sym}; use hir::{Body, HirId, MatchSource, Pat}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -134,16 +134,16 @@ impl<'tcx> OffendingFilterExpr<'tcx> { _ => map_arg, } // .map(|y| y[.acceptable_method()].unwrap()) - && let simple_equal = (path_to_local_id(receiver, filter_param_id) - && path_to_local_id(map_arg_peeled, map_param_id)) + && let simple_equal = (receiver.res_local_id() == Some(filter_param_id) + && map_arg_peeled.res_local_id() == Some(map_param_id)) && let eq_fallback = (|a: &Expr<'_>, b: &Expr<'_>| { // in `filter(|x| ..)`, replace `*x` with `x` let a_path = if !is_filter_param_ref && let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind { expr_path } else { a }; // let the filter closure arg and the map closure arg be equal - path_to_local_id(a_path, filter_param_id) - && path_to_local_id(b, map_param_id) + a_path.res_local_id() == Some(filter_param_id) + && b.res_local_id() == Some(map_param_id) && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b) }) && (simple_equal @@ -166,7 +166,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> { let expr_uses_local = |pat: &Pat<'_>, expr: &Expr<'_>| { if let PatKind::TupleStruct(QPath::Resolved(_, path), [subpat], _) = pat.kind && let PatKind::Binding(_, local_id, ident, _) = subpat.kind - && path_to_local_id(expr.peel_blocks(), local_id) + && expr.peel_blocks().res_local_id() == Some(local_id) && let Some(local_variant_def_id) = path.res.opt_def_id() && local_variant_def_id == variant_def_id { @@ -204,7 +204,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> { _ => return None, }; - if path_to_local_id(scrutinee, map_param_id) + if scrutinee.res_local_id() == Some(map_param_id) // else branch should be a `panic!` or `unreachable!` macro call && let Some(mac) = root_macro_call(else_.peel_blocks().span) && (is_panic(cx, mac.def_id) || cx.tcx.opt_item_name(mac.def_id) == Some(sym::unreachable)) @@ -247,7 +247,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> { } else if matching_root_macro_call(cx, expr.span, sym::matches_macro).is_some() // we know for a fact that the wildcard pattern is the second arm && let ExprKind::Match(scrutinee, [arm, _], _) = expr.kind - && path_to_local_id(scrutinee, filter_param_id) + && scrutinee.res_local_id() == Some(filter_param_id) && let PatKind::TupleStruct(QPath::Resolved(_, path), ..) = arm.pat.kind && let Some(variant_def_id) = path.res.opt_def_id() { diff --git a/clippy_lints/src/methods/manual_inspect.rs b/clippy_lints/src/methods/manual_inspect.rs index bc96815944d5f..e506b8e55b5fb 100644 --- a/clippy_lints/src/methods/manual_inspect.rs +++ b/clippy_lints/src/methods/manual_inspect.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::get_field_by_name; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures}; -use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id, sym}; +use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, is_diag_trait_item, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{BindingMode, BorrowKind, ByRef, ClosureKind, Expr, ExprKind, Mutability, Node, PatKind}; @@ -29,7 +30,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: && let ExprKind::Block(block, _) = body.value.kind && let Some(final_expr) = block.expr && !block.stmts.is_empty() - && path_to_local_id(final_expr, arg_id) + && final_expr.res_local_id() == Some(arg_id) && typeck.expr_adjustments(final_expr).is_empty() { let mut requires_copy = false; @@ -46,7 +47,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: if let ExprKind::Closure(c) = e.kind { // Nested closures don't need to treat returns specially. let _: Option = for_each_expr(cx, cx.tcx.hir_body(c.body).value, |e| { - if path_to_local_id(e, arg_id) { + if e.res_local_id() == Some(arg_id) { let (kind, same_ctxt) = check_use(cx, e); match (kind, same_ctxt && e.span.ctxt() == ctxt) { (_, false) | (UseKind::Deref | UseKind::Return(..), true) => { @@ -64,7 +65,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: }); } else if matches!(e.kind, ExprKind::Ret(_)) { ret_count += 1; - } else if path_to_local_id(e, arg_id) { + } else if e.res_local_id() == Some(arg_id) { let (kind, same_ctxt) = check_use(cx, e); match (kind, same_ctxt && e.span.ctxt() == ctxt) { (UseKind::Return(..), false) => { diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs index 80b955829f1fd..a8e30e44488cc 100644 --- a/clippy_lints/src/methods/manual_ok_or.rs +++ b/clippy_lints/src/methods/manual_ok_or.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::path_to_local_id; -use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; use rustc_errors::Applicability; use rustc_hir::LangItem::{ResultErr, ResultOk}; @@ -60,7 +59,7 @@ fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { && let ExprKind::Call(callee, [ok_arg]) = body.value.kind && callee.res(cx).ctor_parent(cx).is_lang_item(cx, ResultOk) { - path_to_local_id(ok_arg, param_id) + ok_arg.res_local_id() == Some(param_id) } else { false } diff --git a/clippy_lints/src/methods/needless_character_iteration.rs b/clippy_lints/src/methods/needless_character_iteration.rs index d161c002d083f..948ed8a25746e 100644 --- a/clippy_lints/src/methods/needless_character_iteration.rs +++ b/clippy_lints/src/methods/needless_character_iteration.rs @@ -1,4 +1,4 @@ -use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, HirId, StmtKind, UnOp}; use rustc_lint::LateContext; @@ -9,7 +9,7 @@ use super::NEEDLESS_CHARACTER_ITERATION; use super::utils::get_last_chain_binding_hir_id; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{path_to_local_id, peel_blocks, sym}; +use clippy_utils::{peel_blocks, sym}; fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> { while let ExprKind::AddrOf(_, _, e) = expr.kind { @@ -33,7 +33,7 @@ fn handle_expr( // `is_ascii`, then only `.all()` should warn. if revert != is_all && method.ident.name == sym::is_ascii - && path_to_local_id(receiver, first_param) + && receiver.res_local_id() == Some(first_param) && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() && *char_arg_ty.kind() == ty::Char && let Some(snippet) = before_chars.get_source_text(cx) @@ -77,7 +77,7 @@ fn handle_expr( // `is_ascii`, then only `.all()` should warn. if revert != is_all && fn_path.ty_rel_def(cx).is_diag_item(cx, sym::char_is_ascii) - && path_to_local_id(peels_expr_ref(arg), first_param) + && peels_expr_ref(arg).res_local_id() == Some(first_param) && let Some(snippet) = before_chars.get_source_text(cx) { span_lint_and_sugg( diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 2997167f4a58a..1e1888592dcee 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -7,8 +7,7 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{has_non_owning_mutable_access, make_normalized_projection, make_projection}; use clippy_utils::{ - CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local_id, - sym, + CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, sym, }; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; @@ -345,7 +344,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { return; } - if path_to_local_id(recv, self.target) { + if recv.res_local_id() == Some(self.target) { if self .illegal_mutable_capture_ids .intersection(&self.current_mutably_captured_ids) @@ -401,7 +400,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { } } // Check if the collection is used for anything else - if path_to_local_id(expr, self.target) { + if expr.res_local_id() == Some(self.target) { self.seen_other = true; } else { walk_expr(self, expr); @@ -463,7 +462,7 @@ impl<'tcx> Visitor<'tcx> for UsedCountVisitor<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if path_to_local_id(expr, self.id) { + if expr.res_local_id() == Some(self.id) { self.count += 1; } else { walk_expr(self, expr); @@ -548,7 +547,7 @@ impl<'tcx> Visitor<'tcx> for IteratorMethodCheckVisitor<'_, 'tcx> { && (recv.hir_id == self.hir_id_of_expr || self .hir_id_of_let_binding - .is_some_and(|hid| path_to_local_id(recv, hid))) + .is_some_and(|hid| recv.res_local_id() == Some(hid))) && !is_trait_method(self.cx, expr, sym::Iterator) { return ControlFlow::Break(()); diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 4c99c34892f45..3d489075ce8ac 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeDef; +use clippy_utils::peel_blocks; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet; -use clippy_utils::{path_to_local_id, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -51,7 +51,7 @@ pub(super) fn check( match &closure_expr.kind { hir::ExprKind::MethodCall(_, receiver, [], _) => { - if path_to_local_id(receiver, closure_body.params[0].pat.hir_id) + if receiver.res_local_id() == Some(closure_body.params[0].pat.hir_id) && let adj = cx .typeck_results() .expr_adjustments(receiver) @@ -72,7 +72,7 @@ pub(super) fn check( if let hir::ExprKind::Unary(hir::UnOp::Deref, inner1) = inner.kind && let hir::ExprKind::Unary(hir::UnOp::Deref, inner2) = inner1.kind { - path_to_local_id(inner2, closure_body.params[0].pat.hir_id) + inner2.res_local_id() == Some(closure_body.params[0].pat.hir_id) } else { false } diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index a1a482deb2c30..f5d1d3d4703d5 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -1,10 +1,11 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; use clippy_utils::visitors::{Descend, for_each_expr}; -use clippy_utils::{is_diag_item_method, path_to_local_id, paths, sym}; +use clippy_utils::{is_diag_item_method, paths, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{ @@ -214,7 +215,7 @@ fn indirect_usage<'tcx>( { let mut path_to_binding = None; let _: Option = for_each_expr(cx, init_expr, |e| { - if path_to_local_id(e, binding) { + if e.res_local_id() == Some(binding) { path_to_binding = Some(e); } ControlFlow::Continue(Descend::from(path_to_binding.is_none())) diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index cffb01f1bbc19..15fc1fe8007ef 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,10 +1,10 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint; -use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; -use clippy_utils::{is_trait_method, path_to_local_id, sym}; +use clippy_utils::{is_trait_method, sym}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -97,7 +97,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc match expr.kind { hir::ExprKind::Call(func, args) => { if func.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) { - if path_to_local_id(&args[0], arg_id) { + if args[0].res_local_id() == Some(arg_id) { return (false, false); } return (true, false); @@ -107,7 +107,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc hir::ExprKind::MethodCall(segment, recv, [arg], _) => { if segment.ident.name == sym::then_some && cx.typeck_results().expr_ty(recv).is_bool() - && path_to_local_id(arg, arg_id) + && arg.res_local_id() == Some(arg_id) { (false, true) } else { diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index 8e3cc9abe8328..a57c605a17847 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, strip_pat_refs}; +use clippy_utils::{is_trait_method, peel_blocks, strip_pat_refs}; use rustc_ast::ast; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -74,8 +75,8 @@ fn check_fold_with_op( && let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(param_a.pat).kind && let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(param_b.pat).kind - && path_to_local_id(left_expr, first_arg_id) - && (replacement.has_args || path_to_local_id(right_expr, second_arg_id)) + && left_expr.res_local_id() == Some(first_arg_id) + && (replacement.has_args || right_expr.res_local_id() == Some(second_arg_id)) { let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/unnecessary_map_or.rs b/clippy_lints/src/methods/unnecessary_map_or.rs index bee8b6328a541..0c01be4b18756 100644 --- a/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/clippy_lints/src/methods/unnecessary_map_or.rs @@ -3,11 +3,11 @@ use std::borrow::Cow; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::sugg::{Sugg, make_binop}; use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::visitors::is_local_used; -use clippy_utils::{get_parent_expr, is_from_proc_macro, path_to_local_id}; +use clippy_utils::{get_parent_expr, is_from_proc_macro}; use rustc_ast::LitKind::Bool; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind}; @@ -76,11 +76,11 @@ pub(super) fn check<'a>( // .map_or(true, |x| x != y) // .map_or(true, |x| y != x) - swapped comparison && ((BinOpKind::Eq == op.node && !def_bool) || (BinOpKind::Ne == op.node && def_bool)) - && let non_binding_location = if path_to_local_id(l, hir_id) { r } else { l } + && let non_binding_location = if l.res_local_id() == Some(hir_id) { r } else { l } && switch_to_eager_eval(cx, non_binding_location) - // xor, because if its both then that's a strange edge case and + // if its both then that's a strange edge case and // we can just ignore it, since by default clippy will error on this - && (path_to_local_id(l, hir_id) ^ path_to_local_id(r, hir_id)) + && (l.res_local_id() == Some(hir_id)) != (r.res_local_id() == Some(hir_id)) && !is_local_used(cx, non_binding_location, hir_id) && let typeck_results = cx.typeck_results() && let l_ty = typeck_results.expr_ty(l) diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index e56f4b80d0172..b9c87d0a5c87e 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs, should_call_clone_as_function}; -use clippy_utils::{get_parent_expr, is_diag_trait_item, path_to_local_id, peel_blocks, strip_pat_refs}; +use clippy_utils::{get_parent_expr, is_diag_trait_item, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{self as hir, LangItem}; use rustc_lint::LateContext; @@ -137,7 +138,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { // no autoderefs && !cx.typeck_results().expr_adjustments(obj).iter() .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) - && path_to_local_id(obj, local_id) + && obj.res_local_id() == Some(local_id) { true } else { @@ -146,7 +147,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { }, hir::ExprKind::Call(call, [recv]) => { if let hir::ExprKind::Path(qpath) = call.kind - && path_to_local_id(recv, local_id) + && recv.res_local_id() == Some(local_id) { check_qpath(cx, qpath, call.hir_id) } else { diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index 9b670266d0a9c..1e1b124b44866 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -1,5 +1,5 @@ -use clippy_utils::res::MaybeDef; -use clippy_utils::{get_parent_expr, path_to_local_id, usage}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::{get_parent_expr, usage}; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, QPath, Stmt, StmtKind}; use rustc_lint::LateContext; @@ -130,7 +130,7 @@ impl<'tcx> CloneOrCopyVisitor<'_, 'tcx> { fn is_binding(&self, expr: &Expr<'tcx>) -> bool { self.binding_hir_ids .iter() - .any(|hir_id| path_to_local_id(expr, *hir_id)) + .any(|&hir_id| expr.res_local_id() == Some(hir_id)) } } diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index 6ed2628c37e20..ddd4271960e13 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::res::MaybeResPath; -use clippy_utils::{get_parent_expr, path_to_local_id, sym}; +use clippy_utils::{get_parent_expr, sym}; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, LetStmt, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -326,7 +326,7 @@ impl<'tcx> Visitor<'tcx> for ReadVisitor<'_, 'tcx> { return; } - if path_to_local_id(expr, self.var) + if expr.res_local_id() == Some(self.var) // Check that this is a read, not a write. && !is_in_assignment_position(self.cx, expr) { diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 6c578d39bacbd..fb5f21acf2af2 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{implements_trait, implements_trait_with_env_from_iter, is_copy}; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; -use clippy_utils::{is_self, path_to_local_id, peel_hir_ty_options, strip_pat_refs, sym}; +use clippy_utils::{is_self, peel_hir_ty_options, strip_pat_refs, sym}; use rustc_abi::ExternAbi; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::FnKind; @@ -361,7 +361,7 @@ fn extract_clone_suggestions<'tcx>( let mut spans = Vec::new(); for_each_expr_without_closures(body, |e| { if let ExprKind::MethodCall(seg, recv, [], _) = e.kind - && path_to_local_id(recv, id) + && recv.res_local_id() == Some(id) { if seg.ident.name == sym::capacity { return ControlFlow::Break(()); diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index 63d9271ecf058..784ea34bac58f 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::get_expr_use_or_unification_node; use clippy_utils::res::{MaybeQPath, MaybeResPath}; -use clippy_utils::{get_expr_use_or_unification_node, path_to_local_id}; use core::cell::Cell; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; @@ -396,7 +396,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { }, // Parameter update e.g. `x = x + 1` ExprKind::Assign(lhs, rhs, _) | ExprKind::AssignOp(_, lhs, rhs) - if rhs.hir_id == child_id && path_to_local_id(lhs, id) => + if rhs.hir_id == child_id && lhs.res_local_id() == Some(id) => { return; }, diff --git a/clippy_lints/src/pathbuf_init_then_push.rs b/clippy_lints/src/pathbuf_init_then_push.rs index f561c5c4d3e91..a5e57d97301e3 100644 --- a/clippy_lints/src/pathbuf_init_then_push.rs +++ b/clippy_lints/src/pathbuf_init_then_push.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::{path_to_local_id, sym}; +use clippy_utils::sym; use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -176,7 +176,7 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { if let Some(mut searcher) = self.searcher.take() && let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind && let ExprKind::MethodCall(name, self_arg, [arg_expr], _) = expr.kind - && path_to_local_id(self_arg, searcher.local_id) + && self_arg.res_local_id() == Some(searcher.local_id) && name.ident.name == sym::push { searcher.err_span = searcher.err_span.to(stmt.span); diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index ce8db486de1af..e67ea1f5e370d 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -11,8 +11,8 @@ use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{ eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, - pat_and_expr_can_be_question_mark, path_to_local_id, peel_blocks, peel_blocks_with_stmt, span_contains_cfg, - span_contains_comment, sym, + pat_and_expr_can_be_question_mark, peel_blocks, peel_blocks_with_stmt, span_contains_cfg, span_contains_comment, + sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; @@ -378,7 +378,7 @@ fn check_arm_is_some_or_ok<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &Ar // Extract out `val` && let Some(binding) = extract_binding_pat(val_binding) // Check body is just `=> val` - && path_to_local_id(peel_blocks(arm.body), binding) + && peel_blocks(arm.body).res_local_id() == Some(binding) { true } else { @@ -431,8 +431,10 @@ fn is_local_or_local_into(cx: &LateContext<'_>, expr: &Expr<'_>, val: HirId) -> .and_then(|(fn_def_id, _)| cx.tcx.trait_of_assoc(fn_def_id)) .is_some_and(|trait_def_id| cx.tcx.is_diagnostic_item(sym::Into, trait_def_id)); match expr.kind { - ExprKind::MethodCall(_, recv, [], _) | ExprKind::Call(_, [recv]) => is_into_call && path_to_local_id(recv, val), - _ => path_to_local_id(expr, val), + ExprKind::MethodCall(_, recv, [], _) | ExprKind::Call(_, [recv]) => { + is_into_call && recv.res_local_id() == Some(val) + }, + _ => expr.res_local_id() == Some(val), } } @@ -484,7 +486,7 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, 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::Option, cx, &if_block) && peel_blocks(if_then).res_local_id() == Some(bind_id)) || is_early_return(sym::Result, cx, &if_block)) && if_else .map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))) diff --git a/clippy_lints/src/reserve_after_initialization.rs b/clippy_lints/src/reserve_after_initialization.rs index 51adbbcd58bdb..ce86a9caac754 100644 --- a/clippy_lints/src/reserve_after_initialization.rs +++ b/clippy_lints/src/reserve_after_initialization.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; -use clippy_utils::{is_from_proc_macro, path_to_local_id, sym}; +use clippy_utils::{is_from_proc_macro, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, LetStmt, PatKind, QPath, Stmt, StmtKind}; @@ -125,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for ReserveAfterInitialization { if let Some(searcher) = self.searcher.take() { if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind && let ExprKind::MethodCall(name, self_arg, [space_hint], _) = expr.kind - && path_to_local_id(self_arg, searcher.local_id) + && self_arg.res_local_id() == Some(searcher.local_id) && name.ident.name == sym::reserve && !is_from_proc_macro(cx, expr) { diff --git a/clippy_lints/src/returns/let_and_return.rs b/clippy_lints/src/returns/let_and_return.rs index e2002fb36e5a6..f54a26a77620a 100644 --- a/clippy_lints/src/returns/let_and_return.rs +++ b/clippy_lints/src/returns/let_and_return.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, path_to_local_id, span_contains_cfg}; +use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, span_contains_cfg}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, PatKind, StmtKind}; @@ -21,7 +22,7 @@ pub(super) fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>) && cx.tcx.hir_attrs(local.hir_id).is_empty() && let Some(initexpr) = &local.init && let PatKind::Binding(_, local_id, _, _) = local.pat.kind - && path_to_local_id(retexpr, local_id) + && retexpr.res_local_id() == Some(local_id) && (cx.sess().edition() >= Edition::Edition2024 || !last_statement_borrows(cx, initexpr)) && !initexpr.span.in_external_macro(cx.sess().source_map()) && !retexpr.span.in_external_macro(cx.sess().source_map()) diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 14399867f3181..7fdea6bec5100 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -1,7 +1,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::path_to_local_id; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::visitors::{Descend, Visitable, for_each_expr}; use rustc_data_structures::fx::FxHashMap; @@ -202,7 +202,7 @@ pub fn is_local_used_except<'tcx>( for_each_expr(cx, visitable, |e| { if except.is_some_and(|it| it == e.hir_id) { ControlFlow::Continue(Descend::No) - } else if path_to_local_id(e, id) { + } else if e.res_local_id() == Some(id) { ControlFlow::Break(()) } else { ControlFlow::Continue(Descend::Yes) diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index ea8c56d9a79b6..b25fa0905feb8 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::sugg::Sugg; -use clippy_utils::{SpanlessEq, get_enclosing_block, is_integer_literal, path_to_local_id, span_contains_comment, sym}; +use clippy_utils::{SpanlessEq, get_enclosing_block, is_integer_literal, span_contains_comment, sym}; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind}; @@ -244,7 +244,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) { if self.initialization_found && let ExprKind::MethodCall(path, self_arg, [extend_arg], _) = expr.kind - && path_to_local_id(self_arg, self.vec_alloc.local_id) + && self_arg.res_local_id() == Some(self.vec_alloc.local_id) && path.ident.name == sym::extend && self.is_repeat_take(extend_arg) { @@ -256,7 +256,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'tcx>) { if self.initialization_found && let ExprKind::MethodCall(path, self_arg, [len_arg, fill_arg], _) = expr.kind - && path_to_local_id(self_arg, self.vec_alloc.local_id) + && self_arg.res_local_id() == Some(self.vec_alloc.local_id) && path.ident.name == sym::resize // Check that is filled with 0 && is_integer_literal(fill_arg, 0) diff --git a/clippy_lints/src/string_patterns.rs b/clippy_lints/src/string_patterns.rs index f63e6b3087b9e..e5347bf3e8f07 100644 --- a/clippy_lints/src/string_patterns.rs +++ b/clippy_lints/src/string_patterns.rs @@ -5,9 +5,10 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::{snippet, str_literal_to_char_literal}; +use clippy_utils::sym; use clippy_utils::visitors::{Descend, for_each_expr}; -use clippy_utils::{path_to_local_id, sym}; use itertools::Itertools; use rustc_ast::{BinOpKind, LitKind}; use rustc_errors::Applicability; @@ -146,12 +147,12 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr< if for_each_expr(cx, body.value, |sub_expr| -> ControlFlow<(), Descend> { match sub_expr.kind { ExprKind::Binary(op, left, right) if op.node == BinOpKind::Eq => { - if path_to_local_id(left, binding) + if left.res_local_id() == Some(binding) && let Some(span) = get_char_span(cx, right) { set_char_spans.push(span); ControlFlow::Continue(Descend::No) - } else if path_to_local_id(right, binding) + } else if right.res_local_id() == Some(binding) && let Some(span) = get_char_span(cx, left) { set_char_spans.push(span); @@ -164,7 +165,7 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr< ExprKind::Match(match_value, [arm, _], _) => { if matching_root_macro_call(cx, sub_expr.span, sym::matches_macro).is_none() || arm.guard.is_some() - || !path_to_local_id(match_value, binding) + || match_value.res_local_id() != Some(binding) { return ControlFlow::Break(()); } diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 9532423b73edc..df06982904b37 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::ty::is_uninit_value_valid_for_ty; -use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while, sym}; +use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, peel_hir_expr_while, sym}; use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -140,7 +140,7 @@ enum VecLocation<'tcx> { impl<'tcx> VecLocation<'tcx> { pub fn eq_expr(self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { match self { - VecLocation::Local(hir_id) => path_to_local_id(expr, hir_id), + VecLocation::Local(hir_id) => expr.res_local_id() == Some(hir_id), VecLocation::Expr(self_expr) => SpanlessEq::new(cx).eq_expr(self_expr, expr), } } diff --git a/clippy_lints/src/unused_peekable.rs b/clippy_lints/src/unused_peekable.rs index 5fa16dc5f7a2a..58b86c1cb4a99 100644 --- a/clippy_lints/src/unused_peekable.rs +++ b/clippy_lints/src/unused_peekable.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::ty::peel_and_count_ty_refs; -use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators, sym}; +use clippy_utils::{fn_def_id, is_trait_method, peel_ref_operators, sym}; use rustc_ast::Mutability; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, Expr, ExprKind, HirId, LetStmt, Node, PatKind, PathSegment, StmtKind}; @@ -117,7 +117,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { } fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> ControlFlow<()> { - if path_to_local_id(ex, self.expected_hir_id) { + if ex.res_local_id() == Some(self.expected_hir_id) { for (_, node) in self.cx.tcx.hir_parent_iter(ex.hir_id) { match node { Node::Expr(expr) => { diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index 8d873536295dc..5d074208c029c 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_local_use_after_expr; -use clippy_utils::{get_parent_expr, path_to_local_id, sym}; +use clippy_utils::{get_parent_expr, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -201,7 +202,7 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { if let Some(searcher) = self.searcher.take() { if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind && let ExprKind::MethodCall(name, self_arg, [_], _) = expr.kind - && path_to_local_id(self_arg, searcher.local_id) + && self_arg.res_local_id() == Some(searcher.local_id) && name.ident.name == sym::push { self.searcher = Some(VecPushSearcher { diff --git a/clippy_lints/src/zombie_processes.rs b/clippy_lints/src/zombie_processes.rs index 1f7ea06475d87..0319f3e656e1b 100644 --- a/clippy_lints/src/zombie_processes.rs +++ b/clippy_lints/src/zombie_processes.rs @@ -1,7 +1,7 @@ use ControlFlow::{Break, Continue}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::res::MaybeDef; -use clippy_utils::{fn_def_id, get_enclosing_block, path_to_local_id}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::{fn_def_id, get_enclosing_block}; use rustc_ast::Mutability; use rustc_ast::visit::visit_opt; use rustc_errors::Applicability; @@ -168,7 +168,7 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { return walk_expr(self, ex); } - if path_to_local_id(ex, self.local_id) { + if ex.res_local_id() == Some(self.local_id) { match self.cx.tcx.parent_hir_node(ex.hir_id) { Node::Stmt(Stmt { kind: StmtKind::Semi(_), diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 9437a378c390e..837db8cef6f4e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -430,12 +430,6 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator, id: HirId) -> bool { - expr.res_local_id() == Some(id) -} - /// If the expression is a path to a local (with optional projections), /// returns the canonical `HirId` of the local. /// @@ -1599,7 +1593,7 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc .ctor_parent(cx) .is_lang_item(cx, ResultOk) && let PatKind::Binding(_, hir_id, _, None) = pat[0].kind - && path_to_local_id(arm.body, hir_id) + && arm.body.res_local_id() == Some(hir_id) { return true; } @@ -1904,7 +1898,7 @@ pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr< match (pat.kind, expr.kind) { (PatKind::Binding(_, id, _, _), _) if by_hir => { - path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty() + expr.res_local_id() == Some(id) && cx.typeck_results().expr_adjustments(expr).is_empty() }, (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => { matches!(path.segments, [ segment] if segment.ident.name == ident.name) diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 6eccbcdb12281..e27f1dabeefab 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -1,4 +1,5 @@ use crate::macros::root_macro_call_first_node; +use crate::res::MaybeResPath; use crate::visitors::{Descend, Visitable, for_each_expr, for_each_expr_without_closures}; use crate::{self as utils, get_enclosing_loop_or_multi_call_closure}; use core::ops::ControlFlow; @@ -196,7 +197,7 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { pub fn local_used_in<'tcx>(cx: &LateContext<'tcx>, local_id: HirId, v: impl Visitable<'tcx>) -> bool { for_each_expr(cx, v, |e| { - if utils::path_to_local_id(e, local_id) { + if e.res_local_id() == Some(local_id) { ControlFlow::Break(()) } else { ControlFlow::Continue(()) @@ -222,7 +223,7 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr let mut past_expr = false; for_each_expr(cx, block, |e| { if past_expr { - if utils::path_to_local_id(e, local_id) { + if e.res_local_id() == Some(local_id) { ControlFlow::Break(()) } else { ControlFlow::Continue(Descend::Yes) diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 96f3c1fbe3e5a..84e4f043e34f1 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -1,7 +1,8 @@ +use crate::get_enclosing_block; use crate::msrvs::Msrv; use crate::qualify_min_const_fn::is_stable_const_fn; +use crate::res::MaybeResPath; use crate::ty::needs_ordered_drop; -use crate::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; use rustc_ast::visit::{VisitorResult, try_visit}; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -312,7 +313,7 @@ pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { /// Checks if the given local is used. pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool { for_each_expr(cx, visitable, |e| { - if path_to_local_id(e, id) { + if e.res_local_id() == Some(id) { ControlFlow::Break(()) } else { ControlFlow::Continue(()) @@ -564,7 +565,7 @@ pub fn for_each_local_use_after_expr<'tcx, B>( if self.res.is_break() { return; } - if path_to_local_id(e, self.local_id) { + if e.res_local_id() == Some(self.local_id) { self.res = (self.f)(e); } else { walk_expr(self, e); @@ -740,7 +741,7 @@ pub fn for_each_local_assignment<'tcx, B>( fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) { if let ExprKind::Assign(lhs, rhs, _) = e.kind && self.res.is_continue() - && path_to_local_id(lhs, self.local_id) + && lhs.res_local_id() == Some(self.local_id) { self.res = (self.f)(rhs); self.visit_expr(rhs); @@ -785,7 +786,7 @@ pub fn local_used_once<'tcx>( let mut expr = None; let cf = for_each_expr(cx, visitable, |e| { - if path_to_local_id(e, id) && expr.replace(e).is_some() { + if e.res_local_id() == Some(id) && expr.replace(e).is_some() { ControlFlow::Break(()) } else { ControlFlow::Continue(()) From 2e6729ea641194a1a54ef4bcbc2062e1062cd6a1 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 17:08:35 -0400 Subject: [PATCH 59/89] Remove `is_diag_trait_item` --- clippy_lints/src/assigning_clones.rs | 20 ++++----- clippy_lints/src/format_args.rs | 9 ++-- clippy_lints/src/format_impl.rs | 12 ++++-- clippy_lints/src/manual_clamp.rs | 12 +++--- clippy_lints/src/methods/implicit_clone.rs | 29 ++++++------- clippy_lints/src/methods/manual_inspect.rs | 10 ++--- clippy_lints/src/methods/map_clone.rs | 15 +++---- .../src/methods/suspicious_to_owned.rs | 8 ++-- .../src/methods/unnecessary_to_owned.rs | 43 ++++++++++--------- clippy_lints/src/methods/useless_asref.rs | 16 +++---- clippy_lints/src/non_canonical_impls.rs | 7 +-- clippy_utils/src/lib.rs | 23 ++++------ 12 files changed, 102 insertions(+), 102 deletions(-) diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index 6f5c352c061c8..efce23d13a382 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -2,9 +2,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, sym}; +use clippy_utils::{is_in_test, last_path_segment, local_is_initialized, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -69,15 +69,15 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Assign(lhs, rhs, _) = e.kind && let typeck = cx.typeck_results() - && let (call_kind, fn_name, fn_id, fn_arg, fn_gen_args) = match rhs.kind { + && let (call_kind, fn_name, fn_def, fn_arg, fn_gen_args) = match rhs.kind { ExprKind::Call(f, [arg]) if let ExprKind::Path(fn_path) = &f.kind - && let Some(id) = typeck.qpath_res(fn_path, f.hir_id).opt_def_id() => + && let Some(def) = typeck.qpath_res(fn_path, f.hir_id).opt_def(cx) => { - (CallKind::Ufcs, last_path_segment(fn_path).ident.name, id, arg, typeck.node_args(f.hir_id)) + (CallKind::Ufcs, last_path_segment(fn_path).ident.name, def, arg, typeck.node_args(f.hir_id)) }, - ExprKind::MethodCall(name, recv, [], _) if let Some(id) = typeck.type_dependent_def_id(rhs.hir_id) => { - (CallKind::Method, name.ident.name, id, recv, typeck.node_args(rhs.hir_id)) + ExprKind::MethodCall(name, recv, [], _) if let Some(def) = typeck.type_dependent_def(rhs.hir_id) => { + (CallKind::Method, name.ident.name, def, recv, typeck.node_args(rhs.hir_id)) }, _ => return, } @@ -85,16 +85,16 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { // Don't lint in macros. && ctxt.is_root() && let which_trait = match fn_name { - sym::clone if is_diag_trait_item(cx, fn_id, sym::Clone) => CloneTrait::Clone, + sym::clone if fn_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Clone) => CloneTrait::Clone, sym::to_owned - if is_diag_trait_item(cx, fn_id, sym::ToOwned) + if fn_def.assoc_fn_parent(cx).is_diag_item(cx, sym::ToOwned) && self.msrv.meets(cx, msrvs::CLONE_INTO) => { CloneTrait::ToOwned }, _ => return, } - && let Ok(Some(resolved_fn)) = Instance::try_resolve(cx.tcx, cx.typing_env(), fn_id, fn_gen_args) + && let Ok(Some(resolved_fn)) = Instance::try_resolve(cx.tcx, cx.typing_env(), fn_def.1, fn_gen_args) // 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. diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index d7a9dd0d008a6..011cbf8c5d41c 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -12,7 +12,7 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_diag_trait_item, is_from_proc_macro, is_in_test, trait_ref_of_method}; +use clippy_utils::{is_from_proc_macro, is_in_test, trait_ref_of_method}; use itertools::Itertools; use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions, @@ -498,8 +498,11 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { let cx = self.cx; if !value.span.from_expansion() && let ExprKind::MethodCall(_, receiver, [], to_string_span) = value.kind - && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id) - && is_diag_trait_item(cx, method_def_id, sym::ToString) + && cx + .typeck_results() + .type_dependent_def_id(value.hir_id) + .opt_parent(cx) + .is_diag_item(cx, sym::ToString) && let receiver_ty = cx.typeck_results().expr_ty(receiver) && let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display) && let (n_needed_derefs, target) = diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index 4dde968659e03..903d43e56c4b2 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, is_format_macro, root_macro_call_first_node}; -use clippy_utils::res::MaybeResPath; -use clippy_utils::{get_parent_as_impl, is_diag_trait_item, peel_ref_operators, sym}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::{get_parent_as_impl, peel_ref_operators, sym}; use rustc_ast::{FormatArgsPiece, FormatTrait}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath}; @@ -158,8 +158,12 @@ impl FormatImplExpr<'_, '_> { && path.ident.name == sym::to_string // Is the method a part of the ToString trait? (i.e. not to_string() implemented // separately) - && let Some(expr_def_id) = self.cx.typeck_results().type_dependent_def_id(self.expr.hir_id) - && is_diag_trait_item(self.cx, expr_def_id, sym::ToString) + && self + .cx + .typeck_results() + .type_dependent_def_id(self.expr.hir_id) + .opt_parent(self.cx) + .is_diag_item(self.cx, sym::ToString) // Is the method is called on self && let ExprKind::Path(QPath::Resolved(_, path)) = self_arg.kind && let [segment] = path.segments diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index f7cc23517bcfa..38d603ed39aa1 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -3,13 +3,11 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::higher::If; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::is_const_evaluatable; -use clippy_utils::{ - eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, peel_blocks, peel_blocks_with_stmt, sym, -}; +use clippy_utils::{eq_expr_value, is_in_const_context, is_trait_method, peel_blocks, peel_blocks_with_stmt, sym}; use itertools::Itertools; use rustc_errors::{Applicability, Diag}; use rustc_hir::def::Res; @@ -331,11 +329,11 @@ fn is_call_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) fn segment<'tcx>(cx: &LateContext<'_>, func: &Expr<'tcx>) -> Option> { match func.kind { ExprKind::Path(QPath::Resolved(None, path)) => { - let id = path.res.opt_def_id()?; - match cx.tcx.get_diagnostic_name(id) { + let def = path.res.opt_def(cx)?; + match cx.tcx.get_diagnostic_name(def.1) { Some(sym::cmp_min) => Some(FunctionType::CmpMin), Some(sym::cmp_max) => Some(FunctionType::CmpMax), - _ if is_diag_trait_item(cx, id, sym::Ord) => { + _ if def.assoc_fn_parent(cx).is_diag_item(cx, sym::Ord) => { Some(FunctionType::OrdOrFloat(path.segments.last().expect("infallible"))) }, _ => None, diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 0ba84919395cc..57c0ba25ebbf0 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; +use clippy_utils::sym; use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs}; -use clippy_utils::{is_diag_item_method, is_diag_trait_item, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -10,12 +11,12 @@ use rustc_span::Symbol; use super::IMPLICIT_CLONE; pub fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && is_clone_like(cx, method_name, method_def_id) + if let Some(method_parent_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id).opt_parent(cx) + && is_clone_like(cx, method_name, method_parent_id) && let return_type = cx.typeck_results().expr_ty(expr) && let input_type = cx.typeck_results().expr_ty(recv) && let (input_type, ref_count, _) = peel_and_count_ty_refs(input_type) - && !(ref_count > 0 && is_diag_trait_item(cx, method_def_id, sym::ToOwned)) + && !(ref_count > 0 && method_parent_id.is_diag_item(cx, sym::ToOwned)) && let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did())) && return_type == input_type && let Some(clone_trait) = cx.tcx.lang_items().clone_trait() @@ -40,19 +41,15 @@ pub fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &hir::Expr<'_>, re } /// Returns true if the named method can be used to clone the receiver. -pub fn is_clone_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: hir::def_id::DefId) -> bool { +pub fn is_clone_like(cx: &LateContext<'_>, method_name: Symbol, method_parent_id: hir::def_id::DefId) -> bool { match method_name { - sym::to_os_string => is_diag_item_method(cx, method_def_id, sym::OsStr), - sym::to_owned => is_diag_trait_item(cx, method_def_id, sym::ToOwned), - sym::to_path_buf => is_diag_item_method(cx, method_def_id, sym::Path), - sym::to_string => is_diag_trait_item(cx, method_def_id, sym::ToString), - sym::to_vec => cx - .tcx - .impl_of_assoc(method_def_id) - .filter(|&impl_did| { - cx.tcx.type_of(impl_did).instantiate_identity().is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none() - }) - .is_some(), + sym::to_os_string => method_parent_id.opt_impl_ty(cx).is_diag_item(cx, sym::OsStr), + sym::to_owned => method_parent_id.is_diag_item(cx, sym::ToOwned), + sym::to_path_buf => method_parent_id.opt_impl_ty(cx).is_diag_item(cx, sym::Path), + sym::to_string => method_parent_id.is_diag_item(cx, sym::ToString), + sym::to_vec => method_parent_id + .opt_impl_ty(cx) + .is_some_and(|ty| ty.instantiate_identity().is_slice()), _ => false, } } diff --git a/clippy_lints/src/methods/manual_inspect.rs b/clippy_lints/src/methods/manual_inspect.rs index e506b8e55b5fb..69f90c19cba7e 100644 --- a/clippy_lints/src/methods/manual_inspect.rs +++ b/clippy_lints/src/methods/manual_inspect.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::get_field_by_name; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures}; -use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, is_diag_trait_item, sym}; +use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{BindingMode, BorrowKind, ByRef, ClosureKind, Expr, ExprKind, Mutability, Node, PatKind}; @@ -19,9 +19,9 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: if let ExprKind::Closure(c) = arg.kind && matches!(c.kind, ClosureKind::Closure) && let typeck = cx.typeck_results() - && let Some(fn_id) = typeck.type_dependent_def_id(expr.hir_id) - && (is_diag_trait_item(cx, fn_id, sym::Iterator) - || ((is_diag_item_method(cx, fn_id, sym::Option) || is_diag_item_method(cx, fn_id, sym::Result)) + && let Some(fn_def) = typeck.type_dependent_def(expr.hir_id) + && (fn_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Iterator) + || ((is_diag_item_method(cx, fn_def.1, sym::Option) || is_diag_item_method(cx, fn_def.1, sym::Result)) && msrv.meets(cx, msrvs::OPTION_RESULT_INSPECT))) && let body = cx.tcx.hir_body(c.body) && let [param] = body.params diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 5568f77927ab0..1bc29c9c1dd1c 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::peel_blocks; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_copy, should_call_clone_as_function}; -use clippy_utils::{is_diag_trait_item, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; @@ -19,14 +19,13 @@ use super::MAP_CLONE; // If this `map` is called on an `Option` or a `Result` and the previous call is `as_ref`, we don't // run this lint because it would overlap with `useless_asref` which provides a better suggestion // in this case. -fn should_run_lint(cx: &LateContext<'_>, e: &hir::Expr<'_>, method_id: DefId) -> bool { - if is_diag_trait_item(cx, method_id, sym::Iterator) { +fn should_run_lint(cx: &LateContext<'_>, e: &hir::Expr<'_>, method_parent_id: DefId) -> bool { + if method_parent_id.is_diag_item(cx, sym::Iterator) { return true; } // We check if it's an `Option` or a `Result`. - if let Some(id) = cx.tcx.impl_of_assoc(method_id) { - let identity = cx.tcx.type_of(id).instantiate_identity(); - if !identity.is_diag_item(cx, sym::Option) && !identity.is_diag_item(cx, sym::Result) { + if let Some(ty) = method_parent_id.opt_impl_ty(cx) { + if !ty.is_diag_item(cx, sym::Option) && !ty.is_diag_item(cx, sym::Result) { return false; } } else { @@ -43,8 +42,8 @@ fn should_run_lint(cx: &LateContext<'_>, e: &hir::Expr<'_>, method_id: DefId) -> } pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>, msrv: Msrv) { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) - && should_run_lint(cx, e, method_id) + if let Some(parent_id) = cx.typeck_results().type_dependent_def_id(e.hir_id).opt_parent(cx) + && should_run_lint(cx, e, parent_id) { match arg.kind { hir::ExprKind::Closure(&hir::Closure { body, .. }) => { diff --git a/clippy_lints/src/methods/suspicious_to_owned.rs b/clippy_lints/src/methods/suspicious_to_owned.rs index 9a9c09bc3d062..bcd1f11931fc6 100644 --- a/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/clippy_lints/src/methods/suspicious_to_owned.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_diag_trait_item; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; @@ -11,8 +10,11 @@ use rustc_span::sym; use super::SUSPICIOUS_TO_OWNED; pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) -> bool { - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && is_diag_trait_item(cx, method_def_id, sym::ToOwned) + if cx + .typeck_results() + .type_dependent_def_id(expr.hir_id) + .opt_parent(cx) + .is_diag_item(cx, sym::ToOwned) && let input_type = cx.typeck_results().expr_ty(expr) && input_type.is_diag_item(cx, sym::Cow) { diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 768b286ea1326..a6a39cb6ab30e 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -6,9 +6,7 @@ use clippy_utils::res::MaybeDef; use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_and_count_ty_refs}; use clippy_utils::visitors::find_all_ret_expressions; -use clippy_utils::{ - fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, return_ty, sym, -}; +use clippy_utils::{fn_def_id, get_parent_expr, is_expr_temporary_value, return_ty, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; @@ -34,12 +32,12 @@ pub fn check<'tcx>( args: &'tcx [Expr<'_>], msrv: Msrv, ) { - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + if let Some(method_parent_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id).opt_parent(cx) && args.is_empty() { - if is_cloned_or_copied(cx, method_name, method_def_id) { + if is_cloned_or_copied(cx, method_name, method_parent_id) { unnecessary_iter_cloned::check(cx, expr, method_name, receiver); - } else if is_to_owned_like(cx, expr, method_name, method_def_id) { + } else if is_to_owned_like(cx, expr, method_name, method_parent_id) { if check_split_call_arg(cx, expr, method_name, receiver) { return; } @@ -47,7 +45,7 @@ pub fn check<'tcx>( // `check_addr_of_expr` and `check_into_iter_call_arg` determine whether the call is unnecessary // based on its context, that is, whether it is a referent in an `AddrOf` expression, an // argument in a `into_iter` call, or an argument in the call of some other function. - if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) { + if check_addr_of_expr(cx, expr, method_name, method_parent_id, receiver) { return; } if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) { @@ -70,7 +68,7 @@ fn check_addr_of_expr( cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, - method_def_id: DefId, + method_parent_id: DefId, receiver: &Expr<'_>, ) -> bool { if let Some(parent) = get_parent_expr(cx, expr) @@ -132,7 +130,7 @@ fn check_addr_of_expr( // `redundant_clone`, but copyable arrays are not. && (*referent_ty != receiver_ty || (matches!(referent_ty.kind(), ty::Array(..)) && is_copy(cx, *referent_ty)) - || is_cow_into_owned(cx, method_name, method_def_id)) + || is_cow_into_owned(cx, method_name, method_parent_id)) && let Some(receiver_snippet) = receiver.span.get_source_text(cx) { if receiver_ty == target_ty && n_target_refs >= n_receiver_refs { @@ -158,7 +156,7 @@ fn check_addr_of_expr( // *or* this is a `Cow::into_owned()` call (which would be the wrong into_owned receiver (str != Cow) // but that's ok for Cow::into_owned specifically) && (cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() == target_ty - || is_cow_into_owned(cx, method_name, method_def_id)) + || is_cow_into_owned(cx, method_name, method_parent_id)) { if n_receiver_refs > 0 { span_lint_and_sugg( @@ -614,21 +612,26 @@ fn has_lifetime(ty: Ty<'_>) -> bool { } /// Returns true if the named method is `Iterator::cloned` or `Iterator::copied`. -fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { - matches!(method_name, sym::cloned | sym::copied) && is_diag_trait_item(cx, method_def_id, sym::Iterator) +fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_parent_id: DefId) -> bool { + matches!(method_name, sym::cloned | sym::copied) && method_parent_id.is_diag_item(cx, sym::Iterator) } /// Returns true if the named method can be used to convert the receiver to its "owned" /// representation. -fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool { - is_cow_into_owned(cx, method_name, method_def_id) - || (method_name != sym::to_string && is_clone_like(cx, method_name, method_def_id)) - || is_to_string_on_string_like(cx, call_expr, method_name, method_def_id) +fn is_to_owned_like<'a>( + cx: &LateContext<'a>, + call_expr: &Expr<'a>, + method_name: Symbol, + method_parent_id: DefId, +) -> bool { + is_cow_into_owned(cx, method_name, method_parent_id) + || (method_name != sym::to_string && is_clone_like(cx, method_name, method_parent_id)) + || is_to_string_on_string_like(cx, call_expr, method_name, method_parent_id) } /// Returns true if the named method is `Cow::into_owned`. -fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { - method_name == sym::into_owned && is_diag_item_method(cx, method_def_id, sym::Cow) +fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_parent_id: DefId) -> bool { + method_name == sym::into_owned && method_parent_id.opt_impl_ty(cx).is_diag_item(cx, sym::Cow) } /// Returns true if the named method is `ToString::to_string` and it's called on a type that @@ -637,9 +640,9 @@ fn is_to_string_on_string_like<'a>( cx: &LateContext<'_>, call_expr: &'a Expr<'a>, method_name: Symbol, - method_def_id: DefId, + method_parent_id: DefId, ) -> bool { - if method_name != sym::to_string || !is_diag_trait_item(cx, method_def_id, sym::ToString) { + if method_name != sym::to_string || !method_parent_id.is_diag_item(cx, sym::ToString) { return false; } diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index b9c87d0a5c87e..972304d79e751 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs, should_call_clone_as_function}; -use clippy_utils::{get_parent_expr, is_diag_trait_item, peel_blocks, strip_pat_refs}; +use clippy_utils::{get_parent_expr, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{self as hir, LangItem}; use rustc_lint::LateContext; @@ -43,11 +43,11 @@ fn get_enum_ty(enum_ty: Ty<'_>) -> Option> { pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: Symbol, recvr: &hir::Expr<'_>) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" // check if the call is to the actual `AsRef` or `AsMut` trait - let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else { + let Some(def) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else { return; }; - if is_diag_trait_item(cx, def_id, sym::AsRef) || is_diag_trait_item(cx, def_id, sym::AsMut) { + if def.opt_parent(cx).is_diag_item(cx, sym::AsRef) || def.opt_parent(cx).is_diag_item(cx, sym::AsMut) { // check if the type after `as_ref` or `as_mut` is the same as before let rcv_ty = cx.typeck_results().expr_ty(recvr); let res_ty = cx.typeck_results().expr_ty(expr); @@ -80,10 +80,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: Symbo applicability, ); } - } else if let Some(impl_id) = cx.tcx.impl_of_assoc(def_id) - && let Some(adt) = cx.tcx.type_of(impl_id).instantiate_identity().ty_adt_def() - && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::Option | sym::Result)) - { + } else if matches!( + def.opt_parent(cx).opt_impl_ty(cx).opt_diag_name(cx), + Some(sym::Option | sym::Result) + ) { let rcv_ty = cx.typeck_results().expr_ty(recvr).peel_refs(); let res_ty = cx.typeck_results().expr_ty(expr).peel_refs(); diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index 0c5e4d279f5c7..5c1406d782fd6 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_diag_trait_item, is_from_proc_macro, last_path_segment, std_or_core}; +use clippy_utils::{is_from_proc_macro, last_path_segment, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, LangItem, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -275,8 +275,9 @@ fn expr_is_cmp<'tcx>( }, ExprKind::MethodCall(_, recv, [], _) => { typeck - .type_dependent_def_id(expr.hir_id) - .is_some_and(|def_id| is_diag_trait_item(cx, def_id, sym::Into)) + .type_dependent_def(expr.hir_id) + .assoc_parent(cx) + .is_diag_item(cx, sym::Into) && self_cmp_call(cx, typeck, recv, needs_fully_qualified) }, _ => false, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 837db8cef6f4e..f74923a60ec5b 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -366,19 +366,12 @@ pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbo false } -/// Checks if a method is in a diagnostic item trait -pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { - if let Some(trait_did) = cx.tcx.trait_of_assoc(def_id) { - return cx.tcx.is_diagnostic_item(diag_item, trait_did); - } - false -} - /// Checks if the method call given in `expr` belongs to the given trait. pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { cx.typeck_results() - .type_dependent_def_id(expr.hir_id) - .is_some_and(|did| is_diag_trait_item(cx, did, diag_item)) + .type_dependent_def(expr.hir_id) + .assoc_fn_parent(cx) + .is_diag_item(cx, diag_item) } /// Checks if the `def_id` belongs to a function that is part of a trait impl. @@ -404,8 +397,8 @@ pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { if let ExprKind::Path(ref qpath) = expr.kind { cx.qpath_res(qpath, expr.hir_id) - .opt_def_id() - .is_some_and(|def_id| is_diag_trait_item(cx, def_id, diag_item)) + .assoc_parent(cx) + .is_diag_item(cx, diag_item) } else { false } @@ -573,9 +566,9 @@ pub fn is_default_equivalent_call( whole_call_expr: Option<&Expr<'_>>, ) -> bool { if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind - && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() - && (is_diag_trait_item(cx, repl_def_id, sym::Default) - || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath)) + && let Some(repl_def) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def(cx) + && (repl_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Default) + || is_default_equivalent_ctor(cx, repl_def.1, repl_func_qpath)) { return true; } From e78f86d550f6db4d2ce3988dce1cce5696b65625 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 17:40:04 -0400 Subject: [PATCH 60/89] Remove `is_diag_item_method` --- clippy_lints/src/lines_filter_map_ok.rs | 13 +++++++++---- clippy_lints/src/methods/manual_inspect.rs | 5 +++-- clippy_lints/src/methods/str_splitn.rs | 8 +++++--- clippy_utils/src/lib.rs | 10 ---------- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/lines_filter_map_ok.rs b/clippy_lints/src/lines_filter_map_ok.rs index dacc2c1bdbdac..c1f551f06c4e2 100644 --- a/clippy_lints/src/lines_filter_map_ok.rs +++ b/clippy_lints/src/lines_filter_map_ok.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::{is_diag_item_method, is_trait_method, sym}; +use clippy_utils::{is_trait_method, sym}; use rustc_errors::Applicability; use rustc_hir::{Body, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -120,10 +120,15 @@ fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_name: Symbol) -> params: [param], value, .. } = cx.tcx.hir_body(*body) && let ExprKind::MethodCall(method, receiver, [], _) = value.kind - && receiver.res_local_id() == Some(param.pat.hir_id) - && let Some(method_did) = cx.typeck_results().type_dependent_def_id(value.hir_id) { - is_diag_item_method(cx, method_did, sym::Result) && method.ident.name == sym::ok + method.ident.name == sym::ok + && receiver.res_local_id() == Some(param.pat.hir_id) + && cx + .typeck_results() + .type_dependent_def_id(value.hir_id) + .opt_parent(cx) + .opt_impl_ty(cx) + .is_diag_item(cx, sym::Result) } else { false } diff --git a/clippy_lints/src/methods/manual_inspect.rs b/clippy_lints/src/methods/manual_inspect.rs index 69f90c19cba7e..ac5a372264284 100644 --- a/clippy_lints/src/methods/manual_inspect.rs +++ b/clippy_lints/src/methods/manual_inspect.rs @@ -4,7 +4,7 @@ use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::get_field_by_name; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures}; -use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, sym}; +use clippy_utils::{ExprUseNode, expr_use_ctxt, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{BindingMode, BorrowKind, ByRef, ClosureKind, Expr, ExprKind, Mutability, Node, PatKind}; @@ -21,7 +21,8 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: && let typeck = cx.typeck_results() && let Some(fn_def) = typeck.type_dependent_def(expr.hir_id) && (fn_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Iterator) - || ((is_diag_item_method(cx, fn_def.1, sym::Option) || is_diag_item_method(cx, fn_def.1, sym::Result)) + || ((fn_def.assoc_fn_parent(cx).opt_impl_ty(cx).is_diag_item(cx, sym::Option) + || fn_def.assoc_fn_parent(cx).opt_impl_ty(cx).is_diag_item(cx, sym::Result)) && msrv.meets(cx, msrvs::OPTION_RESULT_INSPECT))) && let body = cx.tcx.hir_body(c.body) && let [param] = body.params diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index f5d1d3d4703d5..eee7fb0c5a813 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -1,11 +1,11 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; use clippy_utils::visitors::{Descend, for_each_expr}; -use clippy_utils::{is_diag_item_method, paths, sym}; +use clippy_utils::{paths, sym}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{ @@ -351,7 +351,9 @@ fn parse_iter_usage<'tcx>( && cx .typeck_results() .type_dependent_def_id(e.hir_id) - .is_some_and(|id| is_diag_item_method(cx, id, sym::Option)) => + .opt_parent(cx) + .opt_impl_ty(cx) + .is_diag_item(cx, sym::Option) => { (Some(UnwrapKind::Unwrap), e.span) }, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index f74923a60ec5b..8dbc91cfed351 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -356,16 +356,6 @@ pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } } -/// Checks if a method is defined in an impl of a diagnostic item -pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { - if let Some(impl_did) = cx.tcx.impl_of_assoc(def_id) - && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() - { - return cx.tcx.is_diagnostic_item(diag_item, adt.did()); - } - false -} - /// Checks if the method call given in `expr` belongs to the given trait. pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { cx.typeck_results() From d0be3356ba4c2971eb67a972de46a38db459a1e1 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 17:59:40 -0400 Subject: [PATCH 61/89] Remove `is_inherent_method_call` --- clippy_lints/src/floating_point_arithmetic.rs | 7 ++++--- clippy_lints/src/useless_conversion.rs | 6 +++--- clippy_utils/src/lib.rs | 9 --------- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 407a3f1306739..5f022ba307ff9 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -1,9 +1,10 @@ use clippy_utils::consts::Constant::{F32, F64, Int}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::{ - eq_expr_value, get_parent_expr, has_ambiguous_literal_in_expr, higher, is_in_const_context, - is_inherent_method_call, is_no_std_crate, numeric_literal, peel_blocks, sugg, sym, + eq_expr_value, get_parent_expr, has_ambiguous_literal_in_expr, higher, is_in_const_context, is_no_std_crate, + numeric_literal, peel_blocks, sugg, sym, }; use rustc_ast::ast; use rustc_errors::Applicability; @@ -737,7 +738,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { if let ExprKind::MethodCall(path, receiver, args, _) = &expr.kind { let recv_ty = cx.typeck_results().expr_ty(receiver); - if recv_ty.is_floating_point() && !is_no_std_crate(cx) && is_inherent_method_call(cx, expr) { + if recv_ty.is_floating_point() && !is_no_std_crate(cx) && cx.ty_based_def(expr).opt_parent(cx).is_impl(cx) { match path.ident.name { sym::ln => check_ln1p(cx, expr, receiver), sym::log => check_log_base(cx, expr, receiver, args), diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index dab6a2351a234..9849c4af0318b 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; use clippy_utils::ty::{is_copy, same_type_modulo_regions}; -use clippy_utils::{get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, sym}; +use clippy_utils::{get_parent_expr, is_trait_item, is_trait_method, is_ty_alias, sym}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, PatKind}; @@ -438,7 +438,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } fn has_eligible_receiver(cx: &LateContext<'_>, recv: &Expr<'_>, expr: &Expr<'_>) -> bool { - if is_inherent_method_call(cx, expr) { + if cx.ty_based_def(expr).opt_parent(cx).is_impl(cx) { matches!( cx.typeck_results().expr_ty(recv).opt_diag_name(cx), Some(sym::Option | sym::Result | sym::ControlFlow) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 8dbc91cfed351..e16c3bd06d6ec 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -347,15 +347,6 @@ pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { } } -/// Checks if the given method call expression calls an inherent method. -pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { - cx.tcx.trait_of_assoc(method_id).is_none() - } else { - false - } -} - /// Checks if the method call given in `expr` belongs to the given trait. pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { cx.typeck_results() From e69d88bb10407753a02026a360d47b4cda06cda1 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 18:06:17 -0400 Subject: [PATCH 62/89] Remove `is_trait_method` --- clippy_lints/src/cloned_ref_to_slice_refs.rs | 5 ++- clippy_lints/src/len_zero.rs | 7 ++-- clippy_lints/src/lines_filter_map_ok.rs | 6 +-- .../src/loops/explicit_into_iter_loop.rs | 8 +++- clippy_lints/src/loops/iter_next_loop.rs | 4 +- .../src/loops/while_let_on_iterator.rs | 6 +-- clippy_lints/src/manual_clamp.rs | 12 +++--- clippy_lints/src/manual_hash_one.rs | 6 +-- clippy_lints/src/manual_main_separator_str.rs | 5 ++- .../src/matches/redundant_pattern_match.rs | 6 +-- .../src/methods/cloned_instead_of_copied.rs | 6 ++- .../src/methods/double_ended_iterator_last.rs | 5 ++- clippy_lints/src/methods/filter_map.rs | 10 ++--- .../src/methods/filter_map_bool_then.rs | 7 ++-- .../src/methods/filter_map_identity.rs | 5 ++- clippy_lints/src/methods/filter_map_next.rs | 4 +- clippy_lints/src/methods/flat_map_identity.rs | 7 +++- clippy_lints/src/methods/flat_map_option.rs | 5 +-- clippy_lints/src/methods/inspect_for_each.rs | 4 +- clippy_lints/src/methods/into_iter_on_ref.rs | 4 +- clippy_lints/src/methods/iter_filter.rs | 7 ++-- clippy_lints/src/methods/iter_nth_zero.rs | 5 ++- .../src/methods/iter_out_of_bounds.rs | 5 ++- clippy_lints/src/methods/iter_skip_next.rs | 5 +-- clippy_lints/src/methods/iter_skip_zero.rs | 5 ++- .../src/methods/iterator_step_by_zero.rs | 4 +- clippy_lints/src/methods/manual_inspect.rs | 6 +-- clippy_lints/src/methods/manual_next_back.rs | 6 +-- clippy_lints/src/methods/manual_repeat_n.rs | 5 ++- clippy_lints/src/methods/manual_try_fold.rs | 5 ++- .../src/methods/map_all_any_identity.rs | 7 ++-- clippy_lints/src/methods/map_flatten.rs | 6 +-- clippy_lints/src/methods/map_identity.rs | 6 +-- clippy_lints/src/methods/mod.rs | 38 ++++++++++++------- clippy_lints/src/methods/needless_collect.rs | 18 ++++++--- .../src/methods/range_zip_with_len.rs | 7 ++-- clippy_lints/src/methods/search_is_some.rs | 10 +++-- clippy_lints/src/methods/skip_while_next.rs | 4 +- .../src/methods/string_lit_chars_any.rs | 6 +-- clippy_lints/src/methods/suspicious_map.rs | 8 +++- clippy_lints/src/methods/unbuffered_bytes.rs | 4 +- clippy_lints/src/methods/unit_hash.rs | 4 +- .../src/methods/unnecessary_filter_map.rs | 6 +-- clippy_lints/src/methods/unnecessary_fold.rs | 6 +-- .../src/methods/unnecessary_sort_by.rs | 8 +++- .../src/methods/unused_enumerate_index.rs | 6 +-- .../src/methods/verbose_file_reads.rs | 5 +-- clippy_lints/src/methods/waker_clone_wake.rs | 4 +- clippy_lints/src/minmax.rs | 7 +++- clippy_lints/src/needless_for_each.rs | 5 ++- clippy_lints/src/ptr.rs | 2 +- clippy_lints/src/unused_peekable.rs | 10 +++-- clippy_lints/src/useless_conversion.rs | 10 ++--- clippy_lints/src/vec.rs | 8 ++-- clippy_utils/src/lib.rs | 8 ---- 55 files changed, 212 insertions(+), 166 deletions(-) diff --git a/clippy_lints/src/cloned_ref_to_slice_refs.rs b/clippy_lints/src/cloned_ref_to_slice_refs.rs index 72ab292ee3c66..35b799aefb04c 100644 --- a/clippy_lints/src/cloned_ref_to_slice_refs.rs +++ b/clippy_lints/src/cloned_ref_to_slice_refs.rs @@ -1,9 +1,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::sugg::Sugg; use clippy_utils::visitors::is_const_evaluatable; -use clippy_utils::{is_in_const_context, is_mutable, is_trait_method}; +use clippy_utils::{is_in_const_context, is_mutable}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -73,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> { // check for clones && let ExprKind::MethodCall(_, val, _, _) = item.kind - && is_trait_method(cx, item, sym::Clone) + && cx.ty_based_def(item).opt_parent(cx).is_diag_item(cx, sym::Clone) // check for immutability or purity && (!is_mutable(cx, val) || is_const_evaluatable(cx, val)) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 28a0fbc051158..04a8e4739b853 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; use clippy_utils::ty::implements_trait; -use clippy_utils::{ - fulfill_or_allowed, get_parent_as_impl, is_trait_method, parent_item_name, peel_ref_operators, sym, -}; +use clippy_utils::{fulfill_or_allowed, get_parent_as_impl, parent_item_name, peel_ref_operators, sym}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -204,7 +203,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { } if let ExprKind::MethodCall(method, lhs_expr, [rhs_expr], _) = expr.kind - && is_trait_method(cx, expr, sym::PartialEq) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::PartialEq) && !expr.span.from_expansion() { check_empty_expr( diff --git a/clippy_lints/src/lines_filter_map_ok.rs b/clippy_lints/src/lines_filter_map_ok.rs index c1f551f06c4e2..65e922ac07d36 100644 --- a/clippy_lints/src/lines_filter_map_ok.rs +++ b/clippy_lints/src/lines_filter_map_ok.rs @@ -1,8 +1,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::{is_trait_method, sym}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; +use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Body, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -73,7 +73,7 @@ impl_lint_pass!(LinesFilterMapOk => [LINES_FILTER_MAP_OK]); impl LateLintPass<'_> for LinesFilterMapOk { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::MethodCall(fm_method, fm_receiver, fm_args, fm_span) = expr.kind - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let fm_method_name = fm_method.ident.name && matches!(fm_method_name, sym::filter_map | sym::flat_map | sym::flatten) && cx diff --git a/clippy_lints/src/loops/explicit_into_iter_loop.rs b/clippy_lints/src/loops/explicit_into_iter_loop.rs index 4aa1c2e211d33..daca78e344748 100644 --- a/clippy_lints/src/loops/explicit_into_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_into_iter_loop.rs @@ -1,6 +1,6 @@ use super::EXPLICIT_INTO_ITER_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_context; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -43,7 +43,11 @@ impl AdjustKind { } pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>) { - if !is_trait_method(cx, call_expr, sym::IntoIterator) { + if !cx + .ty_based_def(call_expr) + .opt_parent(cx) + .is_diag_item(cx, sym::IntoIterator) + { return; } diff --git a/clippy_lints/src/loops/iter_next_loop.rs b/clippy_lints/src/loops/iter_next_loop.rs index b8a263817d297..8a4644cdf5efd 100644 --- a/clippy_lints/src/loops/iter_next_loop.rs +++ b/clippy_lints/src/loops/iter_next_loop.rs @@ -1,12 +1,12 @@ use super::ITER_NEXT_LOOP; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_span::sym; pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>) { - if is_trait_method(cx, arg, sym::Iterator) { + if cx.ty_based_def(arg).opt_parent(cx).is_diag_item(cx, sym::Iterator) { span_lint( cx, ITER_NEXT_LOOP, diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index df8b4d1551d42..3ea6ba341bedb 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -2,10 +2,10 @@ use std::ops::ControlFlow; use super::WHILE_LET_ON_ITERATOR; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::visitors::is_res_used; -use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable, is_trait_method}; +use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{Visitor, walk_expr}; @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // check for call to `Iterator::next` && let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind && method_name.ident.name == sym::next - && is_trait_method(cx, let_expr, sym::Iterator) + && cx.ty_based_def(let_expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(iter_expr_struct) = try_parse_iter_expr(cx, iter_expr) // get the loop containing the match expression && !uses_iter(cx, &iter_expr_struct, if_then) diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 38d603ed39aa1..54387e36d6c84 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -3,11 +3,11 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::higher::If; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::is_const_evaluatable; -use clippy_utils::{eq_expr_value, is_in_const_context, is_trait_method, peel_blocks, peel_blocks_with_stmt, sym}; +use clippy_utils::{eq_expr_value, is_in_const_context, peel_blocks, peel_blocks_with_stmt, sym}; use itertools::Itertools; use rustc_errors::{Applicability, Diag}; use rustc_hir::def::Res; @@ -290,10 +290,12 @@ fn is_if_elseif_else_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx /// # ; /// ``` fn is_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { - if let ExprKind::MethodCall(seg_second, receiver, [arg_second], _) = &expr.kind - && (cx.typeck_results().expr_ty_adjusted(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord)) + if let ExprKind::MethodCall(seg_second, receiver, [arg_second], _) = expr.kind + && (cx.typeck_results().expr_ty_adjusted(receiver).is_floating_point() + || cx.ty_based_def(expr).assoc_fn_parent(cx).is_diag_item(cx, sym::Ord)) && let ExprKind::MethodCall(seg_first, input, [arg_first], _) = &receiver.kind - && (cx.typeck_results().expr_ty_adjusted(input).is_floating_point() || is_trait_method(cx, receiver, sym::Ord)) + && (cx.typeck_results().expr_ty_adjusted(input).is_floating_point() + || cx.ty_based_def(receiver).assoc_fn_parent(cx).is_diag_item(cx, sym::Ord)) { let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point(); let (min, max) = match (seg_first.ident.name, seg_second.ident.name) { diff --git a/clippy_lints/src/manual_hash_one.rs b/clippy_lints/src/manual_hash_one.rs index 5c1289231eece..ccb8d4272bfac 100644 --- a/clippy_lints/src/manual_hash_one.rs +++ b/clippy_lints/src/manual_hash_one.rs @@ -1,10 +1,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::SpanRangeExt; +use clippy_utils::sym; use clippy_utils::visitors::{is_local_used, local_used_once}; -use clippy_utils::{is_trait_method, sym}; use rustc_errors::Applicability; use rustc_hir::{BindingMode, ExprKind, LetStmt, Node, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -82,7 +82,7 @@ impl LateLintPass<'_> for ManualHashOne { && !hash_expr.span.from_expansion() && let ExprKind::MethodCall(seg, hashed_value, [ref_to_hasher], _) = hash_expr.kind && seg.ident.name == sym::hash - && is_trait_method(cx, hash_expr, sym::Hash) + && cx.ty_based_def(hash_expr).opt_parent(cx).is_diag_item(cx, sym::Hash) && ref_to_hasher.peel_borrows().res_local_id() == Some(hasher) && let maybe_finish_stmt = stmts.next() diff --git a/clippy_lints/src/manual_main_separator_str.rs b/clippy_lints/src/manual_main_separator_str.rs index f54ccf2c87b0f..e78f6affda2a3 100644 --- a/clippy_lints/src/manual_main_separator_str.rs +++ b/clippy_lints/src/manual_main_separator_str.rs @@ -1,7 +1,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::{is_trait_method, peel_hir_expr_refs}; +use clippy_utils::peel_hir_expr_refs; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, Mutability, QPath}; @@ -52,7 +53,7 @@ impl LateLintPass<'_> for ManualMainSeparatorStr { && path.ident.name == sym::to_string && let ExprKind::Path(QPath::Resolved(None, path)) = receiver.kind && let Res::Def(DefKind::Const, receiver_def_id) = path.res - && is_trait_method(cx, target, sym::ToString) + && cx.ty_based_def(target).opt_parent(cx).is_diag_item(cx, sym::ToString) && cx.tcx.is_diagnostic_item(sym::path_main_separator, receiver_def_id) && let ty::Ref(_, ty, Mutability::Not) = cx.typeck_results().expr_ty_adjusted(expr).kind() && ty.is_str() diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 916ed25cfd748..2ca76a1fe6945 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -1,11 +1,11 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::walk_span_to_context; use clippy_utils::sugg::{Sugg, make_unop}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures}; -use clippy_utils::{higher, is_expn_of, is_trait_method, sym}; +use clippy_utils::{higher, is_expn_of, sym}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; @@ -217,7 +217,7 @@ fn find_method_sugg_for_if_let<'tcx>( if keyword == "while" && let ExprKind::MethodCall(method_path, _, [], _) = let_expr.kind && method_path.ident.name == sym::next - && is_trait_method(cx, let_expr, sym::Iterator) + && cx.ty_based_def(let_expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return; } diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs index f50fb627b89a0..d80d6f7810f53 100644 --- a/clippy_lints/src/methods/cloned_instead_of_copied.rs +++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::{get_iterator_item_ty, is_copy}; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -19,7 +19,9 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, { subst.type_at(0) }, - _ if is_trait_method(cx, expr, sym::Iterator) && msrv.meets(cx, msrvs::ITERATOR_COPIED) => { + _ if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && msrv.meets(cx, msrvs::ITERATOR_COPIED) => + { match get_iterator_item_ty(cx, recv_ty) { // ::Item Some(ty) => ty, diff --git a/clippy_lints/src/methods/double_ended_iterator_last.rs b/clippy_lints/src/methods/double_ended_iterator_last.rs index 578865c329180..8ba8264b713c4 100644 --- a/clippy_lints/src/methods/double_ended_iterator_last.rs +++ b/clippy_lints/src/methods/double_ended_iterator_last.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::{has_non_owning_mutable_access, implements_trait}; -use clippy_utils::{is_mutable, is_trait_method, path_to_local_with_projections, sym}; +use clippy_utils::{is_mutable, path_to_local_with_projections, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, Node, PatKind}; use rustc_lint::LateContext; @@ -13,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Exp let typeck = cx.typeck_results(); // if the "last" method is that of Iterator - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) // if self implements DoubleEndedIterator && let Some(deiter_id) = cx.tcx.get_diagnostic_item(sym::DoubleEndedIterator) && let self_type = cx.typeck_results().expr_ty(self_expr) diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 53ce09c16e441..26b19848fe1b6 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::{is_panic, matching_root_macro_call, root_macro_call}; -use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::{indent_of, reindent_multiline, snippet}; -use clippy_utils::{SpanlessEq, higher, is_trait_method, peel_blocks, sym}; +use clippy_utils::{SpanlessEq, higher, peel_blocks, sym}; use hir::{Body, HirId, MatchSource, Pat}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -266,7 +266,7 @@ fn is_filter_some_map_unwrap( filter_arg: &Expr<'_>, map_arg: &Expr<'_>, ) -> bool { - let iterator = is_trait_method(cx, expr, sym::Iterator); + let iterator = cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator); let option = cx.typeck_results().expr_ty(filter_recv).is_diag_item(cx, sym::Option); (iterator || option) && is_option_filter_map(cx, filter_arg, map_arg) @@ -275,7 +275,7 @@ fn is_filter_some_map_unwrap( /// is `filter(|x| x.is_ok()).map(|x| x.unwrap())` fn is_filter_ok_map_unwrap(cx: &LateContext<'_>, expr: &Expr<'_>, filter_arg: &Expr<'_>, map_arg: &Expr<'_>) -> bool { // result has no filter, so we only check for iterators - let iterator = is_trait_method(cx, expr, sym::Iterator); + let iterator = cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator); iterator && is_ok_filter_map(cx, filter_arg, map_arg) } @@ -398,7 +398,7 @@ fn is_find_or_filter<'a>( filter_arg: &Expr<'_>, map_arg: &Expr<'_>, ) -> Option<(Ident, CheckResult<'a>)> { - if is_trait_method(cx, map_recv, sym::Iterator) + if cx.ty_based_def(map_recv).opt_parent(cx).is_diag_item(cx, sym::Iterator) // filter(|x| ...is_some())... && let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind && let filter_body = cx.tcx.hir_body(filter_body_id) diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs index 94944bd9445b8..b803d1a3309da 100644 --- a/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/clippy_lints/src/methods/filter_map_bool_then.rs @@ -1,10 +1,9 @@ use super::FILTER_MAP_BOOL_THEN; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::ty::is_copy; -use clippy_utils::{ - CaptureKind, can_move_expr_to_closure, contains_return, is_from_proc_macro, is_trait_method, peel_blocks, -}; +use clippy_utils::{CaptureKind, can_move_expr_to_closure, contains_return, is_from_proc_macro, peel_blocks}; use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -16,7 +15,7 @@ use rustc_span::{Span, sym}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &Expr<'_>, call_span: Span) { if !expr.span.in_external_macro(cx.sess().source_map()) - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let ExprKind::Closure(closure) = arg.kind && let body = cx.tcx.hir_body(closure.body) && let value = peel_blocks(body.value) diff --git a/clippy_lints/src/methods/filter_map_identity.rs b/clippy_lints/src/methods/filter_map_identity.rs index b04d761d4860e..c6e30710eef90 100644 --- a/clippy_lints/src/methods/filter_map_identity.rs +++ b/clippy_lints/src/methods/filter_map_identity.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_expr_identity_function, is_expr_untyped_identity_function, is_trait_method}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::{is_expr_identity_function, is_expr_untyped_identity_function}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::ExprKind; @@ -19,7 +20,7 @@ fn is_identity(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) { - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(applicability) = is_identity(cx, filter_map_arg) { // check if the iterator is from an empty array, see issue #12653 diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index 9f3c346042ff9..698025d58e543 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::is_trait_method; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir as hir; @@ -16,7 +16,7 @@ pub(super) fn check<'tcx>( arg: &'tcx hir::Expr<'_>, msrv: Msrv, ) { - if is_trait_method(cx, expr, sym::Iterator) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { if !msrv.meets(cx, msrvs::ITERATOR_FIND_MAP) { return; } diff --git a/clippy_lints/src/methods/flat_map_identity.rs b/clippy_lints/src/methods/flat_map_identity.rs index 0c2ecfbc8ffdf..043adb8434a0d 100644 --- a/clippy_lints/src/methods/flat_map_identity.rs +++ b/clippy_lints/src/methods/flat_map_identity.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_expr_untyped_identity_function, is_trait_method}; +use clippy_utils::is_expr_untyped_identity_function; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -14,7 +15,9 @@ pub(super) fn check<'tcx>( flat_map_arg: &'tcx hir::Expr<'_>, flat_map_span: Span, ) { - if is_trait_method(cx, expr, sym::Iterator) && is_expr_untyped_identity_function(cx, flat_map_arg) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && is_expr_untyped_identity_function(cx, flat_map_arg) + { span_lint_and_sugg( cx, FLAT_MAP_IDENTITY, diff --git a/clippy_lints/src/methods/flat_map_option.rs b/clippy_lints/src/methods/flat_map_option.rs index 3a7892715ed7d..fb0e8f9d91b5a 100644 --- a/clippy_lints/src/methods/flat_map_option.rs +++ b/clippy_lints/src/methods/flat_map_option.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -10,7 +9,7 @@ use rustc_span::{Span, sym}; use super::FLAT_MAP_OPTION; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) { - if !is_trait_method(cx, expr, sym::Iterator) { + if !cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return; } let arg_ty = cx.typeck_results().expr_ty_adjusted(arg); diff --git a/clippy_lints/src/methods/inspect_for_each.rs b/clippy_lints/src/methods/inspect_for_each.rs index 3706f3b670ba1..194ddf45e6f11 100644 --- a/clippy_lints/src/methods/inspect_for_each.rs +++ b/clippy_lints/src/methods/inspect_for_each.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::{Span, sym}; @@ -8,7 +8,7 @@ use super::INSPECT_FOR_EACH; /// lint use of `inspect().for_each()` for `Iterators` pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, inspect_span: Span) { - if is_trait_method(cx, expr, sym::Iterator) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { let msg = "called `inspect(..).for_each(..)` on an `Iterator`"; let hint = "move the code from `inspect(..)` to `for_each(..)` and remove the `inspect(..)`"; span_lint_and_help( diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs index 661e2824144c0..c4b116af48713 100644 --- a/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::has_iter_method; use rustc_errors::Applicability; use rustc_hir as hir; @@ -13,7 +13,7 @@ use super::INTO_ITER_ON_REF; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Span, receiver: &hir::Expr<'_>) { let self_ty = cx.typeck_results().expr_ty_adjusted(receiver); if let ty::Ref(..) = self_ty.kind() - && is_trait_method(cx, expr, sym::IntoIterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IntoIterator) && let Some((kind, method_name)) = ty_has_iter_method(cx, self_ty) { span_lint_and_sugg( diff --git a/clippy_lints/src/methods/iter_filter.rs b/clippy_lints/src/methods/iter_filter.rs index adeff375c8aad..8d95b70c6a4bc 100644 --- a/clippy_lints/src/methods/iter_filter.rs +++ b/clippy_lints/src/methods/iter_filter.rs @@ -1,3 +1,4 @@ +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::get_iterator_item_ty; use hir::ExprKind; use rustc_lint::{LateContext, LintContext}; @@ -6,7 +7,7 @@ use super::{ITER_FILTER_IS_OK, ITER_FILTER_IS_SOME}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline}; -use clippy_utils::{get_parent_expr, is_trait_method, peel_blocks, span_contains_comment, sym}; +use clippy_utils::{get_parent_expr, peel_blocks, span_contains_comment, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::QPath; @@ -107,7 +108,7 @@ fn parent_is_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { if let Some(expr) = get_parent_expr(cx, expr) && let ExprKind::MethodCall(path, _, [_], _) = expr.kind && path.ident.name == sym::map - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return true; } @@ -141,7 +142,7 @@ fn expression_type( filter_arg: &hir::Expr<'_>, filter_span: Span, ) -> Option { - if !is_trait_method(cx, expr, sym::Iterator) + if !cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) || parent_is_map(cx, expr) || span_contains_comment(cx.sess().source_map(), filter_span.with_hi(expr.span.hi())) { diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs index 0f8abd0172423..e0107b75e4142 100644 --- a/clippy_lints/src/methods/iter_nth_zero.rs +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -1,7 +1,8 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lang_item_or_ctor; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_lang_item_or_ctor, is_trait_method}; use hir::{LangItem, OwnerNode}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -13,7 +14,7 @@ use super::ITER_NTH_ZERO; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { if let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) && let def_id = item.owner_id.to_def_id() - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval_local(arg, expr.span.ctxt()) && !is_lang_item_or_ctor(cx, def_id, LangItem::IteratorNext) { diff --git a/clippy_lints/src/methods/iter_out_of_bounds.rs b/clippy_lints/src/methods/iter_out_of_bounds.rs index fa8f9d640ee6e..8cff3bd815a1b 100644 --- a/clippy_lints/src/methods/iter_out_of_bounds.rs +++ b/clippy_lints/src/methods/iter_out_of_bounds.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::expr_or_init; use clippy_utils::higher::VecArgs; -use clippy_utils::{expr_or_init, is_trait_method}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -58,7 +59,7 @@ fn check<'tcx>( message: &'static str, note: &'static str, ) { - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(len) = get_iterator_length(cx, recv) && let Some(skipped) = expr_as_u128(cx, arg) && skipped > len diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs index cf88cab4a77ef..10e58b015762e 100644 --- a/clippy_lints/src/methods/iter_skip_next.rs +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_trait_method; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir as hir; @@ -12,7 +11,7 @@ use super::ITER_SKIP_NEXT; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { // lint if caller of skip is an Iterator - if is_trait_method(cx, expr, sym::Iterator) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { let mut application = Applicability::MachineApplicable; span_lint_and_then( cx, diff --git a/clippy_lints/src/methods/iter_skip_zero.rs b/clippy_lints/src/methods/iter_skip_zero.rs index 663e34437a307..cae31e7c9606e 100644 --- a/clippy_lints/src/methods/iter_skip_zero.rs +++ b/clippy_lints/src/methods/iter_skip_zero.rs @@ -1,6 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{is_from_proc_macro, is_trait_method}; +use clippy_utils::is_from_proc_macro; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -10,7 +11,7 @@ use super::ITER_SKIP_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg_expr: &Expr<'_>) { if !expr.span.from_expansion() - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(arg) = ConstEvalCtxt::new(cx) .eval_local(arg_expr, expr.span.ctxt()) .and_then(|constant| { diff --git a/clippy_lints/src/methods/iterator_step_by_zero.rs b/clippy_lints/src/methods/iterator_step_by_zero.rs index 90d5d9df55eed..11dde2429adfa 100644 --- a/clippy_lints/src/methods/iterator_step_by_zero.rs +++ b/clippy_lints/src/methods/iterator_step_by_zero.rs @@ -1,6 +1,6 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -8,7 +8,7 @@ use rustc_span::sym; use super::ITERATOR_STEP_BY_ZERO; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) { span_lint( diff --git a/clippy_lints/src/methods/manual_inspect.rs b/clippy_lints/src/methods/manual_inspect.rs index ac5a372264284..1a5b180b0c86f 100644 --- a/clippy_lints/src/methods/manual_inspect.rs +++ b/clippy_lints/src/methods/manual_inspect.rs @@ -20,9 +20,9 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: && matches!(c.kind, ClosureKind::Closure) && let typeck = cx.typeck_results() && let Some(fn_def) = typeck.type_dependent_def(expr.hir_id) - && (fn_def.assoc_fn_parent(cx).is_diag_item(cx, sym::Iterator) - || ((fn_def.assoc_fn_parent(cx).opt_impl_ty(cx).is_diag_item(cx, sym::Option) - || fn_def.assoc_fn_parent(cx).opt_impl_ty(cx).is_diag_item(cx, sym::Result)) + && (fn_def.opt_parent(cx).is_diag_item(cx, sym::Iterator) + || ((fn_def.opt_parent(cx).opt_impl_ty(cx).is_diag_item(cx, sym::Option) + || fn_def.opt_parent(cx).opt_impl_ty(cx).is_diag_item(cx, sym::Result)) && msrv.meets(cx, msrvs::OPTION_RESULT_INSPECT))) && let body = cx.tcx.hir_body(c.body) && let [param] = body.params diff --git a/clippy_lints/src/methods/manual_next_back.rs b/clippy_lints/src/methods/manual_next_back.rs index 9a03559b22377..b064f978588c4 100644 --- a/clippy_lints/src/methods/manual_next_back.rs +++ b/clippy_lints/src/methods/manual_next_back.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -20,8 +20,8 @@ pub(super) fn check<'tcx>( .tcx .get_diagnostic_item(sym::DoubleEndedIterator) .is_some_and(|double_ended_iterator| implements_trait(cx, rev_recv_ty, double_ended_iterator, &[])) - && is_trait_method(cx, rev_call, sym::Iterator) - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(rev_call).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/manual_repeat_n.rs b/clippy_lints/src/methods/manual_repeat_n.rs index 83b57cca17bf1..1a7628ed43a4f 100644 --- a/clippy_lints/src/methods/manual_repeat_n.rs +++ b/clippy_lints/src/methods/manual_repeat_n.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_context}; -use clippy_utils::{expr_use_ctxt, fn_def_id, is_trait_method, std_or_core}; +use clippy_utils::{expr_use_ctxt, fn_def_id, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -17,7 +18,7 @@ pub(super) fn check<'tcx>( msrv: Msrv, ) { if !expr.span.from_expansion() - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let ExprKind::Call(_, [repeat_arg]) = repeat_expr.kind && let Some(def_id) = fn_def_id(cx, repeat_expr) && cx.tcx.is_diagnostic_item(sym::iter_repeat, def_id) diff --git a/clippy_lints/src/methods/manual_try_fold.rs b/clippy_lints/src/methods/manual_try_fold.rs index 23dba47f60f40..f2e127bedde56 100644 --- a/clippy_lints/src/methods/manual_try_fold.rs +++ b/clippy_lints/src/methods/manual_try_fold.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_from_proc_macro, is_trait_method}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind}; @@ -20,7 +21,7 @@ pub(super) fn check<'tcx>( msrv: Msrv, ) { if !fold_span.in_external_macro(cx.sess().source_map()) - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let init_ty = cx.typeck_results().expr_ty(init) && let Some(try_trait) = cx.tcx.lang_items().try_trait() && implements_trait(cx, init_ty, try_trait, &[]) diff --git a/clippy_lints/src/methods/map_all_any_identity.rs b/clippy_lints/src/methods/map_all_any_identity.rs index 92b273f557186..ad950f75f8130 100644 --- a/clippy_lints/src/methods/map_all_any_identity.rs +++ b/clippy_lints/src/methods/map_all_any_identity.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_expr_identity_function; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{is_expr_identity_function, is_trait_method}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -19,8 +20,8 @@ pub(super) fn check( any_arg: &Expr<'_>, method: &str, ) { - if is_trait_method(cx, expr, sym::Iterator) - && is_trait_method(cx, recv, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && cx.ty_based_def(recv).opt_parent(cx).is_diag_item(cx, sym::Iterator) && is_expr_identity_function(cx, any_arg) && let map_any_call_span = map_call_span.with_hi(any_call_span.hi()) && let Some(map_arg) = map_arg.span.get_source_text(cx) diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index 18827d71110ec..e4ae14b6cf59c 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_trait_method, span_contains_comment}; +use clippy_utils::span_contains_comment; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -40,7 +40,7 @@ fn try_get_caller_ty_name_and_method_name( caller_expr: &Expr<'_>, map_arg: &Expr<'_>, ) -> Option<(&'static str, &'static str)> { - if is_trait_method(cx, expr, sym::Iterator) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { if is_map_to_option(cx, map_arg) { // `(...).map(...)` has type `impl Iterator> Some(("Iterator", "filter_map")) diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs index f68967c750ef7..fa394526bdf1e 100644 --- a/clippy_lints/src/methods/map_identity.rs +++ b/clippy_lints/src/methods/map_identity.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_copy; -use clippy_utils::{is_expr_untyped_identity_function, is_mutable, is_trait_method, path_to_local_with_projections}; +use clippy_utils::{is_expr_untyped_identity_function, is_mutable, path_to_local_with_projections}; use rustc_errors::Applicability; use rustc_hir::{self as hir, ExprKind, Node, PatKind}; use rustc_lint::{LateContext, LintContext}; @@ -22,7 +22,7 @@ pub(super) fn check( ) { let caller_ty = cx.typeck_results().expr_ty(caller); - if (is_trait_method(cx, expr, sym::Iterator) + if (cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) || caller_ty.is_diag_item(cx, sym::Result) || caller_ty.is_diag_item(cx, sym::Option)) && is_expr_untyped_identity_function(cx, map_arg) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 91cee886bbef8..c9066be51c44b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -152,7 +152,8 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::macros::FormatArgsStorage; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::{contains_return, is_trait_method, iter_input_pats, peel_blocks, sym}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::{contains_return, iter_input_pats, peel_blocks, sym}; pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; use rustc_data_structures::fx::FxHashSet; use rustc_hir::{self as hir, Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; @@ -5051,7 +5052,7 @@ impl Methods { cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv); option_as_ref_cloned::check(cx, recv, span); }, - (sym::collect, []) if is_trait_method(cx, expr, sym::Iterator) => { + (sym::collect, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) => { needless_collect::check(cx, span, expr, recv, call_span); match method_call(recv) { Some((name @ (sym::cloned | sym::copied), recv2, [], _, _)) => { @@ -5072,17 +5073,26 @@ impl Methods { _ => {}, } }, - (sym::count, []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) { - Some((sym::cloned, recv2, [], _, _)) => { - iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::RmCloned, false); - }, - Some((name2 @ (sym::into_iter | sym::iter | sym::iter_mut), recv2, [], _, _)) => { - iter_count::check(cx, expr, recv2, name2); - }, - Some((sym::map, _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg), - Some((sym::filter, recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg), - Some((sym::bytes, recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2), - _ => {}, + (sym::count, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) => { + match method_call(recv) { + Some((sym::cloned, recv2, [], _, _)) => { + iter_overeager_cloned::check( + cx, + expr, + recv, + recv2, + iter_overeager_cloned::Op::RmCloned, + false, + ); + }, + Some((name2 @ (sym::into_iter | sym::iter | sym::iter_mut), recv2, [], _, _)) => { + iter_count::check(cx, expr, recv2, name2); + }, + Some((sym::map, _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg), + Some((sym::filter, recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg), + Some((sym::bytes, recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2), + _ => {}, + } }, (sym::min | sym::max, [arg]) => { unnecessary_min_or_max::check(cx, expr, name, recv, arg); @@ -5465,7 +5475,7 @@ impl Methods { } unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some"); }, - (sym::try_into, []) if is_trait_method(cx, expr, sym::TryInto) => { + (sym::try_into, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::TryInto) => { unnecessary_fallible_conversions::check_method(cx, expr); }, (sym::to_owned, []) => { diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 1e1888592dcee..4f005103d23f8 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -2,13 +2,11 @@ use std::ops::ControlFlow; use super::NEEDLESS_COLLECT; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; -use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{has_non_owning_mutable_access, make_normalized_projection, make_projection}; -use clippy_utils::{ - CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, sym, -}; +use clippy_utils::{CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, sym}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; @@ -337,7 +335,11 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { if let ExprKind::MethodCall(method_name, recv, args, _) = &expr.kind { if args.is_empty() && method_name.ident.name == sym::collect - && is_trait_method(self.cx, expr, sym::Iterator) + && self + .cx + .ty_based_def(expr) + .opt_parent(self.cx) + .is_diag_item(self.cx, sym::Iterator) { self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(recv)); self.visit_expr(recv); @@ -548,7 +550,11 @@ impl<'tcx> Visitor<'tcx> for IteratorMethodCheckVisitor<'_, 'tcx> { || self .hir_id_of_let_binding .is_some_and(|hid| recv.res_local_id() == Some(hid))) - && !is_trait_method(self.cx, expr, sym::Iterator) + && !self + .cx + .ty_based_def(expr) + .opt_parent(self.cx) + .is_diag_item(self.cx, sym::Iterator) { return ControlFlow::Break(()); } else if let ExprKind::Assign(place, value, _span) = &expr.kind diff --git a/clippy_lints/src/methods/range_zip_with_len.rs b/clippy_lints/src/methods/range_zip_with_len.rs index e13df18333e46..de4207c2abf45 100644 --- a/clippy_lints/src/methods/range_zip_with_len.rs +++ b/clippy_lints/src/methods/range_zip_with_len.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{SpanRangeExt as _, snippet_with_applicability}; -use clippy_utils::{SpanlessEq, get_parent_expr, higher, is_integer_const, is_trait_method, sym}; +use clippy_utils::{SpanlessEq, get_parent_expr, higher, is_integer_const, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, Pat, PatKind, QPath}; use rustc_lint::LateContext; @@ -8,7 +9,7 @@ use rustc_lint::LateContext; use super::RANGE_ZIP_WITH_LEN; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, zip_arg: &'tcx Expr<'_>) { - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) // range expression in `.zip()` call: `0..x.len()` && let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg) && is_integer_const(cx, start, 0) @@ -82,7 +83,7 @@ fn for_loop_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Optio /// them to a closure, return the pattern of the closure. fn methods_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Pat<'tcx>> { if let Some(parent_expr) = get_parent_expr(cx, expr) - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let ExprKind::MethodCall(method, recv, [arg], _) = parent_expr.kind && recv.hir_id == expr.hir_id && matches!( diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 95f7a3ebbd300..c9c75f3f38e2d 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::deref_closure_args; -use clippy_utils::{is_receiver_of_method_call, is_trait_method, strip_pat_refs, sym}; +use clippy_utils::{is_receiver_of_method_call, strip_pat_refs, sym}; use hir::ExprKind; use rustc_errors::Applicability; use rustc_hir as hir; @@ -27,7 +27,11 @@ pub(super) fn check<'tcx>( ) { let option_check_method = if is_some { "is_some" } else { "is_none" }; // lint if caller of search is an Iterator - if is_trait_method(cx, is_some_recv, sym::Iterator) { + if cx + .ty_based_def(is_some_recv) + .opt_parent(cx) + .is_diag_item(cx, sym::Iterator) + { let msg = format!("called `{option_check_method}()` after searching an `Iterator` with `{search_method}`"); let search_snippet = snippet(cx, search_arg.span, ".."); if search_snippet.lines().count() <= 1 { diff --git a/clippy_lints/src/methods/skip_while_next.rs b/clippy_lints/src/methods/skip_while_next.rs index 9f0b6c34ea2ef..2452c499b9cee 100644 --- a/clippy_lints/src/methods/skip_while_next.rs +++ b/clippy_lints/src/methods/skip_while_next.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -9,7 +9,7 @@ use super::SKIP_WHILE_NEXT; /// lint use of `skip_while().next()` for `Iterators` pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { // lint if caller of `.skip_while().next()` is an Iterator - if is_trait_method(cx, expr, sym::Iterator) { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { span_lint_and_help( cx, SKIP_WHILE_NEXT, diff --git a/clippy_lints/src/methods/string_lit_chars_any.rs b/clippy_lints/src/methods/string_lit_chars_any.rs index 660ecbc5e6ceb..48e89c2998efa 100644 --- a/clippy_lints/src/methods/string_lit_chars_any.rs +++ b/clippy_lints/src/methods/string_lit_chars_any.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{is_from_proc_macro, is_trait_method}; use itertools::Itertools; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -20,7 +20,7 @@ pub(super) fn check<'tcx>( body: &Expr<'_>, msrv: Msrv, ) { - if is_trait_method(cx, expr, sym::Iterator) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) && let PatKind::Binding(_, arg, _, _) = param.pat.kind && let ExprKind::Lit(lit_kind) = recv.kind && let LitKind::Str(val, _) = lit_kind.node diff --git a/clippy_lints/src/methods/suspicious_map.rs b/clippy_lints/src/methods/suspicious_map.rs index 788014d9bb632..ece97c1f758c0 100644 --- a/clippy_lints/src/methods/suspicious_map.rs +++ b/clippy_lints/src/methods/suspicious_map.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::expr_or_init; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::usage::mutated_variables; -use clippy_utils::{expr_or_init, is_trait_method}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -8,7 +9,10 @@ use rustc_span::sym; use super::SUSPICIOUS_MAP; pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) { - if is_trait_method(cx, count_recv, sym::Iterator) + if cx + .ty_based_def(count_recv) + .opt_parent(cx) + .is_diag_item(cx, sym::Iterator) && let hir::ExprKind::Closure(closure) = expr_or_init(cx, map_arg).kind && let closure_body = cx.tcx.hir_body(closure.body) && !cx.typeck_results().expr_ty(closure_body.value).is_unit() diff --git a/clippy_lints/src/methods/unbuffered_bytes.rs b/clippy_lints/src/methods/unbuffered_bytes.rs index dd5566f8c8baf..fdddd5e2771db 100644 --- a/clippy_lints/src/methods/unbuffered_bytes.rs +++ b/clippy_lints/src/methods/unbuffered_bytes.rs @@ -1,6 +1,6 @@ use super::UNBUFFERED_BYTES; use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::implements_trait; use rustc_hir as hir; use rustc_lint::LateContext; @@ -8,7 +8,7 @@ use rustc_span::sym; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { // Lint if the `.bytes()` call is from the `Read` trait and the implementor is not buffered. - if is_trait_method(cx, expr, sym::IoRead) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IoRead) && let Some(buf_read) = cx.tcx.get_diagnostic_item(sym::IoBufRead) && let ty = cx.typeck_results().expr_ty_adjusted(recv) && !implements_trait(cx, ty, buf_read, &[]) diff --git a/clippy_lints/src/methods/unit_hash.rs b/clippy_lints/src/methods/unit_hash.rs index 3c7955bc46981..9defd5626eb47 100644 --- a/clippy_lints/src/methods/unit_hash.rs +++ b/clippy_lints/src/methods/unit_hash.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -9,7 +9,7 @@ use rustc_span::sym; use super::UNIT_HASH; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) { - if is_trait_method(cx, expr, sym::Hash) && cx.typeck_results().expr_ty(recv).is_unit() { + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Hash) && cx.typeck_results().expr_ty(recv).is_unit() { span_lint_and_then( cx, UNIT_HASH, diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 15fc1fe8007ef..d9d642015063e 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,10 +1,10 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint; -use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath, MaybeTypeckRes}; +use clippy_utils::sym; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; -use clippy_utils::{is_trait_method, sym}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -20,7 +20,7 @@ pub(super) fn check<'tcx>( arg: &'tcx hir::Expr<'tcx>, name: Symbol, ) { - if !is_trait_method(cx, expr, sym::Iterator) { + if !cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return; } diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index a57c605a17847..bd471e0b18e3d 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_trait_method, peel_blocks, strip_pat_refs}; +use clippy_utils::{peel_blocks, strip_pat_refs}; use rustc_ast::ast; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -116,7 +116,7 @@ pub(super) fn check( fold_span: Span, ) { // Check that this is a call to Iterator::fold rather than just some function called fold - if !is_trait_method(cx, expr, sym::Iterator) { + if !cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { return; } diff --git a/clippy_lints/src/methods/unnecessary_sort_by.rs b/clippy_lints/src/methods/unnecessary_sort_by.rs index baaa36ed235ec..4a3e4c092f3b3 100644 --- a/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::std_or_core; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_trait_method, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; use rustc_lint::LateContext; @@ -139,7 +140,10 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Exp ] = &closure_body.params && let ExprKind::MethodCall(method_path, left_expr, [right_expr], _) = closure_body.value.kind && method_path.ident.name == sym::cmp - && is_trait_method(cx, closure_body.value, sym::Ord) + && cx + .ty_based_def(closure_body.value) + .opt_parent(cx) + .is_diag_item(cx, sym::Ord) { let (closure_body, closure_arg, reverse) = if mirrored_exprs(left_expr, left_ident, right_expr, right_ident) { ( diff --git a/clippy_lints/src/methods/unused_enumerate_index.rs b/clippy_lints/src/methods/unused_enumerate_index.rs index 554db52653a35..a7d9b2e0fab01 100644 --- a/clippy_lints/src/methods/unused_enumerate_index.rs +++ b/clippy_lints/src/methods/unused_enumerate_index.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild}; +use clippy_utils::{expr_or_init, pat_is_wild}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind}; use rustc_lint::LateContext; @@ -42,7 +42,7 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, // If we call a method on a `std::iter::Enumerate` instance if recv_ty.is_diag_item(cx, sym::Enumerate) // If we are calling a method of the `Iterator` trait - && is_trait_method(cx, call_expr, sym::Iterator) + && cx.ty_based_def(call_expr).opt_parent(cx).is_diag_item(cx, 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) diff --git a/clippy_lints/src/methods/verbose_file_reads.rs b/clippy_lints/src/methods/verbose_file_reads.rs index d9927b6e368bd..5727843302d6a 100644 --- a/clippy_lints/src/methods/verbose_file_reads.rs +++ b/clippy_lints/src/methods/verbose_file_reads.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_trait_method; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -19,7 +18,7 @@ pub(super) fn check<'tcx>( recv: &'tcx Expr<'_>, (msg, help): (&'static str, &'static str), ) { - if is_trait_method(cx, expr, sym::IoRead) + if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IoRead) && matches!(recv.kind, ExprKind::Path(QPath::Resolved(None, _))) && cx .typeck_results() diff --git a/clippy_lints/src/methods/waker_clone_wake.rs b/clippy_lints/src/methods/waker_clone_wake.rs index b5f34a9be2e23..3081d7f91f067 100644 --- a/clippy_lints/src/methods/waker_clone_wake.rs +++ b/clippy_lints/src/methods/waker_clone_wake.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' if let Some(did) = ty.ty_adt_def() && cx.tcx.is_diagnostic_item(sym::Waker, did.did()) && let ExprKind::MethodCall(_, waker_ref, &[], _) = recv.kind - && is_trait_method(cx, recv, sym::Clone) + && cx.ty_based_def(recv).opt_parent(cx).is_diag_item(cx, sym::Clone) { let mut applicability = Applicability::MachineApplicable; let snippet = snippet_with_applicability(cx, waker_ref.span.source_callsite(), "..", &mut applicability); diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index f9a7c562c7a59..ba62853c74571 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -1,6 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_trait_method, sym}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::sym; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -78,7 +79,9 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons } }, ExprKind::MethodCall(path, receiver, args @ [_], _) => { - if cx.typeck_results().expr_ty(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord) { + if cx.typeck_results().expr_ty(receiver).is_floating_point() + || cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Ord) + { match path.ident.name { sym::max => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Max), sym::min => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Min), diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 3a6ccc2bca998..d03188f1d39b8 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -1,3 +1,4 @@ +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind, TyKind}; @@ -7,8 +8,8 @@ use rustc_span::Span; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::sym; use clippy_utils::ty::has_iter_method; -use clippy_utils::{is_trait_method, sym}; declare_clippy_lint! { /// ### What it does @@ -65,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..) ) && method_name.ident.name == sym::for_each - && is_trait_method(cx, expr, sym::Iterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) // Checks the type of the `iter` method receiver is NOT a user defined type. && has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some() // Skip the lint if the body is not block because this is simpler than `for` loop. diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 71c1a40ca8132..8446b6fbbea57 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -618,7 +618,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[ // Some methods exist on both `[T]` and `Vec`, such as `len`, where the receiver type // doesn't coerce to a slice and our adjusted type check below isn't enough, // but it would still be valid to call with a slice - if is_allowed_vec_method(self.cx, use_expr) { + if is_allowed_vec_method(use_expr) { return; } } diff --git a/clippy_lints/src/unused_peekable.rs b/clippy_lints/src/unused_peekable.rs index 58b86c1cb4a99..668663e4cf794 100644 --- a/clippy_lints/src/unused_peekable.rs +++ b/clippy_lints/src/unused_peekable.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::ty::peel_and_count_ty_refs; -use clippy_utils::{fn_def_id, is_trait_method, peel_ref_operators, sym}; +use clippy_utils::{fn_def_id, peel_ref_operators, sym}; use rustc_ast::Mutability; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, Expr, ExprKind, HirId, LetStmt, Node, PatKind, PathSegment, StmtKind}; @@ -161,7 +161,11 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { // foo.some_method() excluding Iterator methods if remaining_args.iter().any(|arg| arg_is_mut_peekable(self.cx, arg)) - && !is_trait_method(self.cx, expr, sym::Iterator) + && !self + .cx + .ty_based_def(expr) + .opt_parent(self.cx) + .is_diag_item(self.cx, sym::Iterator) { return ControlFlow::Break(()); } diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 9849c4af0318b..f3b1b681c809a 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -3,7 +3,7 @@ use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; use clippy_utils::ty::{is_copy, same_type_modulo_regions}; -use clippy_utils::{get_parent_expr, is_trait_item, is_trait_method, is_ty_alias, sym}; +use clippy_utils::{get_parent_expr, is_trait_item, is_ty_alias, sym}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, PatKind}; @@ -132,7 +132,7 @@ fn into_iter_bound<'tcx>( /// Extracts the receiver of a `.into_iter()` method call. fn into_iter_call<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Option<&'hir Expr<'hir>> { if let ExprKind::MethodCall(name, recv, [], _) = expr.kind - && is_trait_method(cx, expr, sym::IntoIterator) + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IntoIterator) && name.ident.name == sym::into_iter { Some(recv) @@ -203,7 +203,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { }, ExprKind::MethodCall(name, recv, [], _) => { - if is_trait_method(cx, e, sym::Into) && name.ident.name == sym::into { + if cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::Into) && name.ident.name == sym::into { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); if same_type_modulo_regions(a, b) { @@ -363,7 +363,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { ); } } - if is_trait_method(cx, e, sym::TryInto) + if cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::TryInto) && name.ident.name == sym::try_into && let a = cx.typeck_results().expr_ty(e) && let b = cx.typeck_results().expr_ty(recv) @@ -444,7 +444,7 @@ fn has_eligible_receiver(cx: &LateContext<'_>, recv: &Expr<'_>, expr: &Expr<'_>) Some(sym::Option | sym::Result | sym::ControlFlow) ) } else { - is_trait_method(cx, expr, sym::Iterator) + cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) } } diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 52b30ddce12b8..b87db836869d6 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -10,7 +10,7 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; -use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method, span_contains_comment, sym}; +use clippy_utils::{get_parent_expr, higher, is_in_test, span_contains_comment, sym}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -124,7 +124,7 @@ impl UselessVec { if let Some(parent) = get_parent_expr(cx, expr) && (adjusts_to_slice(cx, expr) || matches!(parent.kind, ExprKind::Index(..)) - || is_allowed_vec_method(cx, parent)) + || is_allowed_vec_method(parent)) { ControlFlow::Continue(()) } else { @@ -304,11 +304,11 @@ fn adjusts_to_slice(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { /// Checks if the given expression is a method call to a `Vec` method /// that also exists on slices. If this returns true, it means that /// this expression does not actually require a `Vec` and could just work with an array. -pub fn is_allowed_vec_method(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { +pub fn is_allowed_vec_method(e: &Expr<'_>) -> bool { if let ExprKind::MethodCall(path, _, [], _) = e.kind { matches!(path.ident.name, sym::as_ptr | sym::is_empty | sym::len) } else { - is_trait_method(cx, e, sym::IntoIterator) + false } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index e16c3bd06d6ec..9fe03ad0283d8 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -347,14 +347,6 @@ pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { } } -/// Checks if the method call given in `expr` belongs to the given trait. -pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { - cx.typeck_results() - .type_dependent_def(expr.hir_id) - .assoc_fn_parent(cx) - .is_diag_item(cx, diag_item) -} - /// 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 Node::Item(item) = cx.tcx.parent_hir_node(cx.tcx.local_def_id_to_hir_id(def_id)) From 6bfb524e9ac6160d6f3fb9af9e253173f98515a0 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 15 Sep 2025 18:31:57 -0400 Subject: [PATCH 63/89] Remove `is_trait_item` --- clippy_lints/src/useless_conversion.rs | 9 ++++++--- clippy_utils/src/lib.rs | 19 ------------------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index f3b1b681c809a..0cf5b9431a342 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; +use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath, MaybeTypeckRes}; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; use clippy_utils::ty::{is_copy, same_type_modulo_regions}; -use clippy_utils::{get_parent_expr, is_trait_item, is_ty_alias, sym}; +use clippy_utils::{get_parent_expr, is_ty_alias, sym}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, PatKind}; @@ -180,7 +180,10 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { path.ident.name, sym::map | sym::map_err | sym::map_break | sym::map_continue ) && has_eligible_receiver(cx, recv, e) - && (is_trait_item(cx, arg, sym::Into) || is_trait_item(cx, arg, sym::From)) + && matches!( + arg.res(cx).assoc_parent(cx).opt_diag_name(cx), + Some(sym::Into | sym::From) + ) && let ty::FnDef(_, args) = cx.typeck_results().expr_ty(arg).kind() && let &[from_ty, to_ty] = args.into_type_list(cx.tcx).as_slice() && same_type_modulo_regions(from_ty, to_ty) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 9fe03ad0283d8..8250104b0ca28 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -358,25 +358,6 @@ pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool } } -/// Checks if the given expression is a path referring an item on the trait -/// that is marked with the given diagnostic item. -/// -/// For checking method call expressions instead of path expressions, use -/// [`is_trait_method`]. -/// -/// For example, this can be used to find if an expression like `u64::default` -/// refers to an item of the trait `Default`, which is associated with the -/// `diag_item` of `sym::Default`. -pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { - if let ExprKind::Path(ref qpath) = expr.kind { - cx.qpath_res(qpath, expr.hir_id) - .assoc_parent(cx) - .is_diag_item(cx, diag_item) - } else { - false - } -} - pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> { match *path { QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"), From 097f2fd2f4e9d7760f00a47638fbd886c83f269d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 24 Sep 2025 16:35:46 +0200 Subject: [PATCH 64/89] add missing test for macros The missing external macro test for `non_canonical_clone_impl` was caught thanks to lintcheck -- I went ahead and added all the other combinations of the test while at it --- tests/ui/non_canonical_clone_impl.fixed | 53 ++++++++++---- tests/ui/non_canonical_clone_impl.rs | 56 ++++++++++---- tests/ui/non_canonical_clone_impl.stderr | 14 +++- tests/ui/non_canonical_partial_ord_impl.fixed | 71 ++++++++++++++++++ tests/ui/non_canonical_partial_ord_impl.rs | 73 +++++++++++++++++++ .../ui/non_canonical_partial_ord_impl.stderr | 25 +++++-- 6 files changed, 260 insertions(+), 32 deletions(-) diff --git a/tests/ui/non_canonical_clone_impl.fixed b/tests/ui/non_canonical_clone_impl.fixed index 11616f28825b0..d9be98de69b17 100644 --- a/tests/ui/non_canonical_clone_impl.fixed +++ b/tests/ui/non_canonical_clone_impl.fixed @@ -4,7 +4,7 @@ #![no_main] extern crate proc_macros; -use proc_macros::with_span; +use proc_macros::inline_macros; // lint @@ -100,18 +100,45 @@ impl Clone for Uwu { impl Copy for Uwu {} -// should skip proc macros, see https://github.com/rust-lang/rust-clippy/issues/12788 -#[derive(proc_macro_derive::NonCanonicalClone)] -pub struct G; +#[inline_macros] +mod issue12788 { + use proc_macros::{external, with_span}; -with_span!( - span + // lint -- not an external macro + inline!( + #[derive(Copy)] + pub struct A; - #[derive(Copy)] - struct H; - impl Clone for H { - fn clone(&self) -> Self { - todo!() + impl Clone for A { + fn clone(&self) -> Self { *self } } - } -); + ); + + // do not lint -- should skip external macros + external!( + #[derive(Copy)] + pub struct B; + + impl Clone for B { + fn clone(&self) -> Self { + todo!() + } + } + ); + + // do not lint -- should skip proc macros + #[derive(proc_macro_derive::NonCanonicalClone)] + pub struct C; + + with_span!( + span + + #[derive(Copy)] + struct D; + impl Clone for D { + fn clone(&self) -> Self { + todo!() + } + } + ); +} diff --git a/tests/ui/non_canonical_clone_impl.rs b/tests/ui/non_canonical_clone_impl.rs index 7d101915517fe..3db22bdbcf318 100644 --- a/tests/ui/non_canonical_clone_impl.rs +++ b/tests/ui/non_canonical_clone_impl.rs @@ -4,7 +4,7 @@ #![no_main] extern crate proc_macros; -use proc_macros::with_span; +use proc_macros::inline_macros; // lint @@ -114,18 +114,48 @@ impl Clone for Uwu { impl Copy for Uwu {} -// should skip proc macros, see https://github.com/rust-lang/rust-clippy/issues/12788 -#[derive(proc_macro_derive::NonCanonicalClone)] -pub struct G; +#[inline_macros] +mod issue12788 { + use proc_macros::{external, with_span}; -with_span!( - span + // lint -- not an external macro + inline!( + #[derive(Copy)] + pub struct A; - #[derive(Copy)] - struct H; - impl Clone for H { - fn clone(&self) -> Self { - todo!() + impl Clone for A { + fn clone(&self) -> Self { + //~^ non_canonical_clone_impl + todo!() + } } - } -); + ); + + // do not lint -- should skip external macros + external!( + #[derive(Copy)] + pub struct B; + + impl Clone for B { + fn clone(&self) -> Self { + todo!() + } + } + ); + + // do not lint -- should skip proc macros + #[derive(proc_macro_derive::NonCanonicalClone)] + pub struct C; + + with_span!( + span + + #[derive(Copy)] + struct D; + impl Clone for D { + fn clone(&self) -> Self { + todo!() + } + } + ); +} diff --git a/tests/ui/non_canonical_clone_impl.stderr b/tests/ui/non_canonical_clone_impl.stderr index 5500984527170..cf36a8f49f812 100644 --- a/tests/ui/non_canonical_clone_impl.stderr +++ b/tests/ui/non_canonical_clone_impl.stderr @@ -41,5 +41,17 @@ LL | | *self = source.clone(); LL | | } | |_____^ help: remove it -error: aborting due to 4 previous errors +error: non-canonical implementation of `clone` on a `Copy` type + --> tests/ui/non_canonical_clone_impl.rs:127:37 + | +LL | fn clone(&self) -> Self { + | _____________________________________^ +LL | | +LL | | todo!() +LL | | } + | |_____________^ help: change this to: `{ *self }` + | + = note: this error originates in the macro `__inline_mac_mod_issue12788` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 5 previous errors diff --git a/tests/ui/non_canonical_partial_ord_impl.fixed b/tests/ui/non_canonical_partial_ord_impl.fixed index 6915e1984fb1e..aa23fd99ad77b 100644 --- a/tests/ui/non_canonical_partial_ord_impl.fixed +++ b/tests/ui/non_canonical_partial_ord_impl.fixed @@ -1,5 +1,9 @@ +//@aux-build:proc_macro_derive.rs #![no_main] +extern crate proc_macros; +use proc_macros::inline_macros; + use std::cmp::Ordering; // lint @@ -163,6 +167,73 @@ impl PartialOrd for I { } } +#[inline_macros] +mod issue12788 { + use std::cmp::Ordering; + + use proc_macros::{external, with_span}; + + // lint -- not an external macro + inline!( + #[derive(PartialEq, Eq)] + pub struct A; + + impl Ord for A { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for A { + //~^ non_canonical_partial_ord_impl + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } + } + ); + + // do not lint -- should skip external macros + external!( + #[derive(PartialEq, Eq)] + pub struct B; + + impl Ord for B { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for B { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } + } + + ); + + // do not lint -- should skip proc macros + #[derive(proc_macro_derive::NonCanonicalClone)] + pub struct C; + + with_span!( + span + + #[derive(PartialEq, Eq)] + pub struct D; + + impl Ord for D { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for D { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } + } + + ); +} + // #13640, do not lint #[derive(Eq, PartialEq)] diff --git a/tests/ui/non_canonical_partial_ord_impl.rs b/tests/ui/non_canonical_partial_ord_impl.rs index 7ce4cdc9aec83..da7f73f7c4bef 100644 --- a/tests/ui/non_canonical_partial_ord_impl.rs +++ b/tests/ui/non_canonical_partial_ord_impl.rs @@ -1,5 +1,9 @@ +//@aux-build:proc_macro_derive.rs #![no_main] +extern crate proc_macros; +use proc_macros::inline_macros; + use std::cmp::Ordering; // lint @@ -167,6 +171,75 @@ impl PartialOrd for I { } } +#[inline_macros] +mod issue12788 { + use std::cmp::Ordering; + + use proc_macros::{external, with_span}; + + // lint -- not an external macro + inline!( + #[derive(PartialEq, Eq)] + pub struct A; + + impl Ord for A { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for A { + //~^ non_canonical_partial_ord_impl + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } + } + ); + + // do not lint -- should skip external macros + external!( + #[derive(PartialEq, Eq)] + pub struct B; + + impl Ord for B { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for B { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } + } + + ); + + // do not lint -- should skip proc macros + #[derive(proc_macro_derive::NonCanonicalClone)] + pub struct C; + + with_span!( + span + + #[derive(PartialEq, Eq)] + pub struct D; + + impl Ord for D { + fn cmp(&self, other: &Self) -> Ordering { + todo!(); + } + } + + impl PartialOrd for D { + fn partial_cmp(&self, other: &Self) -> Option { + todo!(); + } + } + + ); +} + // #13640, do not lint #[derive(Eq, PartialEq)] diff --git a/tests/ui/non_canonical_partial_ord_impl.stderr b/tests/ui/non_canonical_partial_ord_impl.stderr index 9bd6b1f726df2..8e55603dd9dac 100644 --- a/tests/ui/non_canonical_partial_ord_impl.stderr +++ b/tests/ui/non_canonical_partial_ord_impl.stderr @@ -1,5 +1,5 @@ error: non-canonical implementation of `partial_cmp` on an `Ord` type - --> tests/ui/non_canonical_partial_ord_impl.rs:16:1 + --> tests/ui/non_canonical_partial_ord_impl.rs:20:1 | LL | / impl PartialOrd for A { LL | | @@ -15,7 +15,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::non_canonical_partial_ord_impl)]` error: non-canonical implementation of `partial_cmp` on an `Ord` type - --> tests/ui/non_canonical_partial_ord_impl.rs:51:1 + --> tests/ui/non_canonical_partial_ord_impl.rs:55:1 | LL | / impl PartialOrd for C { LL | | @@ -32,7 +32,22 @@ LL + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp | error: non-canonical implementation of `partial_cmp` on an `Ord` type - --> tests/ui/non_canonical_partial_ord_impl.rs:198:1 + --> tests/ui/non_canonical_partial_ord_impl.rs:191:9 + | +LL | / impl PartialOrd for A { +LL | | +LL | | fn partial_cmp(&self, other: &Self) -> Option { + | | _____________________________________________________________________- +LL | || todo!(); +LL | || } + | ||_____________- help: change this to: `{ Some(self.cmp(other)) }` +LL | | } + | |__________^ + | + = note: this error originates in the macro `__inline_mac_mod_issue12788` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: non-canonical implementation of `partial_cmp` on an `Ord` type + --> tests/ui/non_canonical_partial_ord_impl.rs:271:1 | LL | / impl PartialOrd for K { LL | | @@ -45,7 +60,7 @@ LL | | } | |__^ error: non-canonical implementation of `partial_cmp` on an `Ord` type - --> tests/ui/non_canonical_partial_ord_impl.rs:216:1 + --> tests/ui/non_canonical_partial_ord_impl.rs:289:1 | LL | / impl PartialOrd for L { LL | | @@ -57,5 +72,5 @@ LL | || } LL | | } | |__^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors From f6665478e17b29cb3c0a555a7b8911bd2e878e27 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 30 Sep 2025 16:07:23 +0200 Subject: [PATCH 65/89] Cleanup: rename `Applicability` variables/parameters --- clippy_lints/src/loops/utils.rs | 8 ++++---- clippy_lints/src/methods/iter_skip_next.rs | 6 +++--- clippy_lints/src/non_std_lazy_statics.rs | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 7bf7565f73c71..56d535c4f2625 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -256,7 +256,7 @@ fn is_conditional(expr: &Expr<'_>) -> bool { /// 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 { +pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applicability: &mut Applicability) -> String { let impls_iterator = cx .tcx .get_diagnostic_item(sym::Iterator) @@ -264,7 +264,7 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic if impls_iterator { format!( "{}", - sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_paren() + sugg::Sugg::hir_with_applicability(cx, arg, "_", applicability).maybe_paren() ) } else { // (&x).into_iter() ==> x.iter() @@ -282,12 +282,12 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic }; format!( "{}.{method_name}()", - sugg::Sugg::hir_with_applicability(cx, caller, "_", applic_ref).maybe_paren(), + sugg::Sugg::hir_with_applicability(cx, caller, "_", applicability).maybe_paren(), ) }, _ => format!( "{}.into_iter()", - sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_paren() + sugg::Sugg::hir_with_applicability(cx, arg, "_", applicability).maybe_paren() ), } } diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs index 10e58b015762e..661188c90ed61 100644 --- a/clippy_lints/src/methods/iter_skip_next.rs +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -12,7 +12,7 @@ use super::ITER_SKIP_NEXT; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { // lint if caller of skip is an Iterator if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) { - let mut application = Applicability::MachineApplicable; + let mut applicability = Applicability::MachineApplicable; span_lint_and_then( cx, ITER_SKIP_NEXT, @@ -24,7 +24,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr && let PatKind::Binding(ann, _, _, _) = pat.kind && ann != BindingMode::MUT { - application = Applicability::Unspecified; + applicability = Applicability::Unspecified; diag.span_help( pat.span, format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")), @@ -35,7 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr expr.span.trim_start(recv.span).unwrap(), "use `nth` instead", format!(".nth({})", snippet(cx, arg.span, "..")), - application, + applicability, ); }, ); diff --git a/clippy_lints/src/non_std_lazy_statics.rs b/clippy_lints/src/non_std_lazy_statics.rs index c9c272abf9e3b..61e4d419fdf06 100644 --- a/clippy_lints/src/non_std_lazy_statics.rs +++ b/clippy_lints/src/non_std_lazy_statics.rs @@ -220,7 +220,7 @@ impl LazyInfo { fn lint(&self, cx: &LateContext<'_>, sugg_map: &FxIndexMap>) { // Applicability might get adjusted to `Unspecified` later if any calls // in `calls_span_and_id` are not replaceable judging by the `sugg_map`. - let mut appl = Applicability::MachineApplicable; + let mut app = Applicability::MachineApplicable; let mut suggs = vec![(self.ty_span_no_args, "std::sync::LazyLock".to_string())]; for (span, def_id) in &self.calls_span_and_id { @@ -229,7 +229,7 @@ impl LazyInfo { suggs.push((*span, sugg)); } else { // If NO suggested replacement, not machine applicable - appl = Applicability::Unspecified; + app = Applicability::Unspecified; } } @@ -240,7 +240,7 @@ impl LazyInfo { self.ty_span_no_args, "this type has been superseded by `LazyLock` in the standard library", |diag| { - diag.multipart_suggestion("use `std::sync::LazyLock` instead", suggs, appl); + diag.multipart_suggestion("use `std::sync::LazyLock` instead", suggs, app); }, ); } From d10ea6eab0062246041d3730f2ccbac268558f48 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 30 Sep 2025 19:13:01 +0200 Subject: [PATCH 66/89] Cleanup: rename `EarlyContext`/`LateContext` parameters --- clippy_lints/src/significant_drop_tightening.rs | 6 +++--- clippy_lints/src/single_char_lifetime_names.rs | 6 +++--- clippy_lints_internal/src/produce_ice.rs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index dcce906499585..c4604fb1558df 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -320,7 +320,7 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { } }, hir::StmtKind::Semi(semi_expr) => { - if has_drop(semi_expr, apa.first_bind_ident, self.cx) { + if has_drop(self.cx, semi_expr, apa.first_bind_ident) { apa.has_expensive_expr_after_last_attr = false; apa.last_stmt_span = DUMMY_SP; return; @@ -417,11 +417,11 @@ fn dummy_stmt_expr<'any>(expr: &'any hir::Expr<'any>) -> hir::Stmt<'any> { } } -fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: Option, lcx: &LateContext<'_>) -> bool { +fn has_drop(cx: &LateContext<'_>, expr: &hir::Expr<'_>, first_bind_ident: Option) -> bool { if let hir::ExprKind::Call(fun, [first_arg]) = expr.kind && let hir::ExprKind::Path(hir::QPath::Resolved(_, fun_path)) = &fun.kind && let Res::Def(DefKind::Fn, did) = fun_path.res - && lcx.tcx.is_diagnostic_item(sym::mem_drop, did) + && cx.tcx.is_diagnostic_item(sym::mem_drop, did) { let has_ident = |local_expr: &hir::Expr<'_>| { if let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &local_expr.kind diff --git a/clippy_lints/src/single_char_lifetime_names.rs b/clippy_lints/src/single_char_lifetime_names.rs index 8c34da0d14a4d..595c75acf031f 100644 --- a/clippy_lints/src/single_char_lifetime_names.rs +++ b/clippy_lints/src/single_char_lifetime_names.rs @@ -40,8 +40,8 @@ declare_clippy_lint! { declare_lint_pass!(SingleCharLifetimeNames => [SINGLE_CHAR_LIFETIME_NAMES]); impl EarlyLintPass for SingleCharLifetimeNames { - fn check_generic_param(&mut self, ctx: &EarlyContext<'_>, param: &GenericParam) { - if param.ident.span.in_external_macro(ctx.sess().source_map()) { + fn check_generic_param(&mut self, cx: &EarlyContext<'_>, param: &GenericParam) { + if param.ident.span.in_external_macro(cx.sess().source_map()) { return; } @@ -51,7 +51,7 @@ impl EarlyLintPass for SingleCharLifetimeNames { { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( - ctx, + cx, SINGLE_CHAR_LIFETIME_NAMES, param.ident.span, "single-character lifetime names are likely uninformative", diff --git a/clippy_lints_internal/src/produce_ice.rs b/clippy_lints_internal/src/produce_ice.rs index 3a813b4b9a223..630843049da25 100644 --- a/clippy_lints_internal/src/produce_ice.rs +++ b/clippy_lints_internal/src/produce_ice.rs @@ -25,9 +25,9 @@ declare_tool_lint! { declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); impl EarlyLintPass for ProduceIce { - fn check_fn(&mut self, ctx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { + fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { if is_trigger_fn(fn_kind) { - ctx.sess() + cx.sess() .dcx() .span_delayed_bug(span, "Would you like some help with that?"); } From 5de7da824c2ddda869be1973c0d468c8ef5a6abb Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 30 Sep 2025 16:08:00 +0200 Subject: [PATCH 67/89] New internal lint: `unusual_names` This lint aims at detecting unusual names used in Clippy source code, such as `appl` or `application` for a `rustc_errors::Applicability` variable, instead of `app` and `applicability` which are commonly used throughout Clippy. This helps maintaining the consistency of the Clippy source code. --- clippy_lints_internal/Cargo.toml | 1 + clippy_lints_internal/src/internal_paths.rs | 5 ++ clippy_lints_internal/src/lib.rs | 3 + clippy_lints_internal/src/unusual_names.rs | 99 +++++++++++++++++++++ clippy_utils/src/sym.rs | 9 ++ 5 files changed, 117 insertions(+) create mode 100644 clippy_lints_internal/src/unusual_names.rs diff --git a/clippy_lints_internal/Cargo.toml b/clippy_lints_internal/Cargo.toml index a8293a1ad395d..16b45322e30f1 100644 --- a/clippy_lints_internal/Cargo.toml +++ b/clippy_lints_internal/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] clippy_config = { path = "../clippy_config" } clippy_utils = { path = "../clippy_utils" } +itertools = "0.12" regex = { version = "1.5" } rustc-semver = "1.1" diff --git a/clippy_lints_internal/src/internal_paths.rs b/clippy_lints_internal/src/internal_paths.rs index dc1e30ab2bdd3..95bdf27b019c6 100644 --- a/clippy_lints_internal/src/internal_paths.rs +++ b/clippy_lints_internal/src/internal_paths.rs @@ -2,13 +2,18 @@ use clippy_utils::paths::{PathLookup, PathNS}; use clippy_utils::{sym, type_path, value_path}; // Paths inside rustc +pub static APPLICABILITY: PathLookup = type_path!(rustc_errors::Applicability); +pub static EARLY_CONTEXT: PathLookup = type_path!(rustc_lint::EarlyContext); pub static EARLY_LINT_PASS: PathLookup = type_path!(rustc_lint::passes::EarlyLintPass); pub static KW_MODULE: PathLookup = type_path!(rustc_span::symbol::kw); +pub static LATE_CONTEXT: PathLookup = type_path!(rustc_lint::LateContext); pub static LINT: PathLookup = type_path!(rustc_lint_defs::Lint); pub static SYMBOL: PathLookup = type_path!(rustc_span::symbol::Symbol); pub static SYMBOL_AS_STR: PathLookup = value_path!(rustc_span::symbol::Symbol::as_str); pub static SYM_MODULE: PathLookup = type_path!(rustc_span::symbol::sym); pub static SYNTAX_CONTEXT: PathLookup = type_path!(rustc_span::hygiene::SyntaxContext); +#[expect(clippy::unnecessary_def_path, reason = "for uniform checking in internal lint")] +pub static TY_CTXT: PathLookup = type_path!(rustc_middle::ty::TyCtxt); // Paths in clippy itself pub static CLIPPY_SYM_MODULE: PathLookup = type_path!(clippy_utils::sym); diff --git a/clippy_lints_internal/src/lib.rs b/clippy_lints_internal/src/lib.rs index 43cde86504f5c..d686ba73387cb 100644 --- a/clippy_lints_internal/src/lib.rs +++ b/clippy_lints_internal/src/lib.rs @@ -41,6 +41,7 @@ mod produce_ice; mod symbols; mod unnecessary_def_path; mod unsorted_clippy_utils_paths; +mod unusual_names; use rustc_lint::{Lint, LintStore}; @@ -59,6 +60,7 @@ static LINTS: &[&Lint] = &[ symbols::SYMBOL_AS_STR, unnecessary_def_path::UNNECESSARY_DEF_PATH, unsorted_clippy_utils_paths::UNSORTED_CLIPPY_UTILS_PATHS, + unusual_names::UNUSUAL_NAMES, ]; pub fn register_lints(store: &mut LintStore) { @@ -74,4 +76,5 @@ pub fn register_lints(store: &mut LintStore) { store.register_late_pass(|_| Box::new(outer_expn_data_pass::OuterExpnDataPass)); store.register_late_pass(|_| Box::new(msrv_attr_impl::MsrvAttrImpl)); store.register_late_pass(|_| Box::new(almost_standard_lint_formulation::AlmostStandardFormulation::new())); + store.register_late_pass(|_| Box::new(unusual_names::UnusualNames)); } diff --git a/clippy_lints_internal/src/unusual_names.rs b/clippy_lints_internal/src/unusual_names.rs new file mode 100644 index 0000000000000..e11a2868fb69c --- /dev/null +++ b/clippy_lints_internal/src/unusual_names.rs @@ -0,0 +1,99 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::paths::PathLookup; +use clippy_utils::sym; +use itertools::Itertools; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, FnDecl, Pat, PatKind, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::kw; +use rustc_span::{Span, Symbol}; + +use crate::internal_paths::{APPLICABILITY, EARLY_CONTEXT, LATE_CONTEXT, TY_CTXT}; + +declare_tool_lint! { + /// ### What it does + /// Checks if variables of some types use the usual name. + /// + /// ### Why is this bad? + /// Restricting the identifiers used for common things in + /// Clippy sources increases consistency. + /// + /// ### Example + /// Check that an `rustc_errors::Applicability` variable is + /// named either `app` or `applicability`, and not + /// `a` or `appl`. + pub clippy::UNUSUAL_NAMES, + Warn, + "commonly used concepts should use usual same variable name.", + report_in_external_macro: true +} + +declare_lint_pass!(UnusualNames => [UNUSUAL_NAMES]); + +const USUAL_NAMES: [(&PathLookup, &str, &[Symbol]); 4] = [ + ( + &APPLICABILITY, + "rustc_errors::Applicability", + &[sym::app, sym::applicability], + ), + (&EARLY_CONTEXT, "rustc_lint::EarlyContext", &[sym::cx]), + (&LATE_CONTEXT, "rustc_lint::LateContext", &[sym::cx]), + (&TY_CTXT, "rustc_middle::ty::TyCtxt", &[sym::tcx]), +]; + +impl<'tcx> LateLintPass<'tcx> for UnusualNames { + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if let StmtKind::Let(let_stmt) = stmt.kind + && let Some(init_expr) = let_stmt.init + { + check_pat_name_for_ty(cx, let_stmt.pat, cx.typeck_results().expr_ty(init_expr), "variable"); + } + } + + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + _decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + _span: Span, + def_id: LocalDefId, + ) { + if matches!(kind, FnKind::Closure) { + return; + } + for (param, ty) in body + .params + .iter() + .zip(cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder().inputs()) + { + check_pat_name_for_ty(cx, param.pat, *ty, "parameter"); + } + } +} + +fn check_pat_name_for_ty(cx: &LateContext<'_>, pat: &Pat<'_>, ty: Ty<'_>, kind: &str) { + if let PatKind::Binding(_, _, ident, _) = pat.kind { + let ty = ty.peel_refs(); + for (usual_ty, ty_str, usual_names) in USUAL_NAMES { + if usual_ty.matches_ty(cx, ty) + && !usual_names.contains(&ident.name) + && ident.name != kw::SelfLower + && !ident.name.as_str().starts_with('_') + { + let usual_names = usual_names.iter().map(|name| format!("`{name}`")).join(" or "); + span_lint_and_help( + cx, + UNUSUAL_NAMES, + ident.span, + format!("unusual name for a {kind} of type `{ty_str}`"), + None, + format!("prefer using {usual_names}"), + ); + } + } + } +} diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 2b22f344e8c02..a14c783c2e93c 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -34,6 +34,7 @@ macro_rules! generate { // // `cargo dev fmt` ensures that the content of the `generate!()` macro call stays sorted. generate! { + Applicability, AsyncReadExt, AsyncWriteExt, BACKSLASH_SINGLE_QUOTE: r"\'", @@ -45,10 +46,12 @@ generate! { Current, DOUBLE_QUOTE: "\"", Deserialize, + EarlyContext, EarlyLintPass, IntoIter, Itertools, LF: "\n", + LateContext, Lazy, Lint, LowerExp, @@ -75,7 +78,9 @@ generate! { Weak, abs, ambiguous_glob_reexports, + app, append, + applicability, arg, as_bytes, as_deref, @@ -125,6 +130,7 @@ generate! { count_ones, create, create_new, + cx, cycle, cyclomatic_complexity, de, @@ -289,8 +295,10 @@ generate! { rsplit_terminator, rsplitn, rsplitn_mut, + rustc_errors, rustc_lint, rustc_lint_defs, + rustc_middle, rustc_span, rustfmt_skip, rwlock, @@ -333,6 +341,7 @@ generate! { symbol, take, take_while, + tcx, then, then_some, to_ascii_lowercase, From 12e2542c558133f66896fa4c3cbe07259559309f Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 18 Sep 2025 18:20:30 +0200 Subject: [PATCH 68/89] Dereference argument of `manual_div_ceil()` if needed --- clippy_lints/src/manual_div_ceil.rs | 13 ++++++++----- tests/ui/manual_div_ceil.fixed | 5 +++++ tests/ui/manual_div_ceil.rs | 5 +++++ tests/ui/manual_div_ceil.stderr | 8 +++++++- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/manual_div_ceil.rs b/clippy_lints/src/manual_div_ceil.rs index ed0cce754b954..ee531741a5153 100644 --- a/clippy_lints/src/manual_div_ceil.rs +++ b/clippy_lints/src/manual_div_ceil.rs @@ -1,7 +1,6 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; use clippy_utils::{SpanlessEq, sym}; use rustc_ast::{BinOpKind, LitIntType, LitKind, UnOp}; @@ -165,6 +164,7 @@ fn build_suggestion( applicability: &mut Applicability, ) { let dividend_sugg = Sugg::hir_with_applicability(cx, lhs, "..", applicability).maybe_paren(); + let rhs_ty = cx.typeck_results().expr_ty(rhs); let type_suffix = if cx.typeck_results().expr_ty(lhs).is_numeric() && matches!( lhs.kind, @@ -182,7 +182,7 @@ fn build_suggestion( } ) ) { - format!("_{}", cx.typeck_results().expr_ty(rhs)) + format!("_{rhs_ty}") } else { String::new() }; @@ -199,9 +199,12 @@ fn build_suggestion( } else { format!("{dividend_sugg_str}{type_suffix}") }; - let divisor_snippet = snippet_with_context(cx, rhs.span, expr.span.ctxt(), "..", applicability); - let sugg = format!("{suggestion_before_div_ceil}.div_ceil({})", divisor_snippet.0); + // Dereference the RHS if it is a reference type + let divisor_snippet = match Sugg::hir_with_context(cx, rhs, expr.span.ctxt(), "_", applicability) { + sugg if rhs_ty.is_ref() => sugg.deref(), + sugg => sugg, + }; span_lint_and_sugg( cx, @@ -209,7 +212,7 @@ fn build_suggestion( expr.span, "manually reimplementing `div_ceil`", "consider using `.div_ceil()`", - sugg, + format!("{suggestion_before_div_ceil}.div_ceil({divisor_snippet})"), *applicability, ); } diff --git a/tests/ui/manual_div_ceil.fixed b/tests/ui/manual_div_ceil.fixed index 58ee6978fc125..cd91be87ec175 100644 --- a/tests/ui/manual_div_ceil.fixed +++ b/tests/ui/manual_div_ceil.fixed @@ -100,3 +100,8 @@ fn issue_13950() { let _ = (-8 + y) / -7; let _ = (y - 8) / -7; } + +fn issue_15705(size: u64, c: &u64) { + let _ = size.div_ceil(*c); + //~^ manual_div_ceil +} diff --git a/tests/ui/manual_div_ceil.rs b/tests/ui/manual_div_ceil.rs index aa0d81b22a0e2..9899c7d775c23 100644 --- a/tests/ui/manual_div_ceil.rs +++ b/tests/ui/manual_div_ceil.rs @@ -100,3 +100,8 @@ fn issue_13950() { let _ = (-8 + y) / -7; let _ = (y - 8) / -7; } + +fn issue_15705(size: u64, c: &u64) { + let _ = (size + c - 1) / c; + //~^ manual_div_ceil +} diff --git a/tests/ui/manual_div_ceil.stderr b/tests/ui/manual_div_ceil.stderr index 9be5a19bf391f..44de3ba99be78 100644 --- a/tests/ui/manual_div_ceil.stderr +++ b/tests/ui/manual_div_ceil.stderr @@ -125,5 +125,11 @@ error: manually reimplementing `div_ceil` LL | let _ = (7 + x) / 8; | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` -error: aborting due to 19 previous errors +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:105:13 + | +LL | let _ = (size + c - 1) / c; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `size.div_ceil(*c)` + +error: aborting due to 20 previous errors From 16880f7594eec31aa9f4a1ad41befe2d1ecc4b1d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 1 Oct 2025 22:54:50 +0200 Subject: [PATCH 69/89] check earlier for `PartialEq` where `Rhs != Self` --- clippy_lints/src/non_canonical_impls.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index 5c1406d782fd6..2ee8ea2a37765 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -5,7 +5,7 @@ use clippy_utils::{is_from_proc_macro, last_path_segment, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, LangItem, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{EarlyBinder, TraitRef, TypeckResults}; +use rustc_middle::ty::{EarlyBinder, TypeckResults}; use rustc_session::declare_lint_pass; use rustc_span::sym; use rustc_span::symbol::kw; @@ -129,11 +129,14 @@ impl LateLintPass<'_> for NonCanonicalImpls { { check_clone_on_copy(cx, impl_item, block); } else if trait_name == Some(sym::PartialOrd) + // If `Self` and `Rhs` are not the same type, then a corresponding `Ord` impl is not possible, + // since it doesn't have an `Rhs` + && let [lhs, rhs] = trait_impl.args.as_slice() && lhs == rhs && impl_item.ident.name == sym::partial_cmp && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) { - check_partial_ord_on_ord(cx, impl_item, item, &trait_impl, body, block); + check_partial_ord_on_ord(cx, impl_item, item, body, block); } } } @@ -179,7 +182,6 @@ fn check_partial_ord_on_ord<'tcx>( cx: &LateContext<'tcx>, impl_item: &ImplItem<'_>, item: &Item<'_>, - trait_impl: &TraitRef<'_>, body: &Body<'_>, block: &Block<'tcx>, ) { @@ -205,13 +207,6 @@ fn check_partial_ord_on_ord<'tcx>( { return; } - // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid - // suggestion tons more complex. - else if let [lhs, rhs, ..] = trait_impl.args.as_slice() - && lhs != rhs - { - return; - } span_lint_and_then( cx, From 3c02c0ef206dd3e6a75c4960230c13173c5e0b87 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 23 Sep 2025 21:15:01 +0200 Subject: [PATCH 70/89] refactor(non_canonical_impls): lint starting from `impl`s --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/non_canonical_impls.rs | 127 ++++++++++++++++++------ 2 files changed, 98 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 149785c594472..dc706603c50e2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -765,7 +765,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(conf))); store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit)); store.register_late_pass(move |_| Box::new(needless_pass_by_ref_mut::NeedlessPassByRefMut::new(conf))); - store.register_late_pass(|_| Box::new(non_canonical_impls::NonCanonicalImpls)); + store.register_late_pass(|tcx| Box::new(non_canonical_impls::NonCanonicalImpls::new(tcx))); store.register_late_pass(move |_| Box::new(single_call_fn::SingleCallFn::new(conf))); store.register_early_pass(move || Box::new(raw_strings::RawStrings::new(conf))); store.register_late_pass(move |_| Box::new(legacy_numeric_constants::LegacyNumericConstants::new(conf))); diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index 2ee8ea2a37765..3285023b34aa8 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -3,10 +3,11 @@ use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::ty::implements_trait; use clippy_utils::{is_from_proc_macro, last_path_segment, std_or_core}; use rustc_errors::Applicability; -use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, LangItem, Node, UnOp}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Block, Body, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, LangItem, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{EarlyBinder, TypeckResults}; -use rustc_session::declare_lint_pass; +use rustc_middle::ty::{EarlyBinder, TyCtxt, TypeckResults}; +use rustc_session::impl_lint_pass; use rustc_span::sym; use rustc_span::symbol::kw; @@ -107,36 +108,96 @@ declare_clippy_lint! { suspicious, "non-canonical implementation of `PartialOrd` on an `Ord` type" } -declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL_PARTIAL_ORD_IMPL]); +impl_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL_PARTIAL_ORD_IMPL]); + +#[expect( + clippy::struct_field_names, + reason = "`_trait` suffix is meaningful on its own, \ + and creating an inner `StoredTraits` struct would just add a level of indirection" +)] +pub(crate) struct NonCanonicalImpls { + partial_ord_trait: Option, + ord_trait: Option, + clone_trait: Option, + copy_trait: Option, +} + +impl NonCanonicalImpls { + pub(crate) fn new(tcx: TyCtxt<'_>) -> Self { + let lang_items = tcx.lang_items(); + Self { + partial_ord_trait: lang_items.partial_ord_trait(), + ord_trait: tcx.get_diagnostic_item(sym::Ord), + clone_trait: lang_items.clone_trait(), + copy_trait: lang_items.copy_trait(), + } + } +} + +/// The traits that this lint looks at +enum Trait { + Clone, + PartialOrd, +} impl LateLintPass<'_> for NonCanonicalImpls { - fn check_impl_item<'tcx>(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'tcx>) { - if let ImplItemKind::Fn(_, impl_item_id) = impl_item.kind - && let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) - && let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) - && let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id) - // NOTE: check this early to avoid expensive checks that come after this one - && matches!(trait_name, Some(sym::Clone | sym::PartialOrd)) + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if let ItemKind::Impl(impl_) = item.kind + // Both `PartialOrd` and `Clone` have one required method, and `PartialOrd` can have 5 methods in total + && (1..=5).contains(&impl_.items.len()) + && let Some(of_trait) = impl_.of_trait + && let Some(trait_did) = of_trait.trait_ref.trait_def_id() + // Check this early to hopefully bail out as soon as possible + && let trait_ = if Some(trait_did) == self.clone_trait { + Trait::Clone + } else if Some(trait_did) == self.partial_ord_trait { + Trait::PartialOrd + } else { + return; + } && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) - && let body = cx.tcx.hir_body(impl_item_id) - && let ExprKind::Block(block, ..) = body.value.kind - && !block.span.in_external_macro(cx.sess().source_map()) - && !is_from_proc_macro(cx, impl_item) { - if trait_name == Some(sym::Clone) - && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) - && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[]) - { - check_clone_on_copy(cx, impl_item, block); - } else if trait_name == Some(sym::PartialOrd) - // If `Self` and `Rhs` are not the same type, then a corresponding `Ord` impl is not possible, - // since it doesn't have an `Rhs` - && let [lhs, rhs] = trait_impl.args.as_slice() && lhs == rhs - && impl_item.ident.name == sym::partial_cmp - && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) - && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) - { - check_partial_ord_on_ord(cx, impl_item, item, body, block); + let mut assoc_fns = impl_ + .items + .iter() + .map(|id| cx.tcx.hir_impl_item(*id)) + .filter_map(|assoc| { + if let ImplItemKind::Fn(_, body_id) = assoc.kind + && let body = cx.tcx.hir_body(body_id) + && let ExprKind::Block(block, ..) = body.value.kind + && !block.span.in_external_macro(cx.sess().source_map()) + { + Some((assoc, body, block)) + } else { + None + } + }); + + match trait_ { + Trait::Clone => { + if let Some(copy_trait) = self.copy_trait + && let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) + && implements_trait(cx, trait_impl.self_ty(), copy_trait, &[]) + { + for (assoc, _, block) in assoc_fns { + check_clone_on_copy(cx, assoc, block); + } + } + }, + Trait::PartialOrd => { + if let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) + // If `Self` and `Rhs` are not the same type, then a corresponding `Ord` impl is not possible, + // since it doesn't have an `Rhs` + && let [lhs, rhs] = trait_impl.args.as_slice() + && lhs == rhs + && let Some(ord_trait) = self.ord_trait + && implements_trait(cx, trait_impl.self_ty(), ord_trait, &[]) + && let Some((assoc, body, block)) = + assoc_fns.find(|(assoc, _, _)| assoc.ident.name == sym::partial_cmp) + { + check_partial_ord_on_ord(cx, assoc, item, body, block); + } + }, } } } @@ -154,6 +215,10 @@ fn check_clone_on_copy(cx: &LateContext<'_>, impl_item: &ImplItem<'_>, block: &B return; } + if is_from_proc_macro(cx, impl_item) { + return; + } + span_lint_and_sugg( cx, NON_CANONICAL_CLONE_IMPL, @@ -165,7 +230,7 @@ fn check_clone_on_copy(cx: &LateContext<'_>, impl_item: &ImplItem<'_>, block: &B ); } - if impl_item.ident.name == sym::clone_from { + if impl_item.ident.name == sym::clone_from && !is_from_proc_macro(cx, impl_item) { span_lint_and_sugg( cx, NON_CANONICAL_CLONE_IMPL, @@ -206,6 +271,8 @@ fn check_partial_ord_on_ord<'tcx>( && expr_is_cmp(cx, ret, impl_item, &mut needs_fully_qualified) { return; + } else if is_from_proc_macro(cx, impl_item) { + return; } span_lint_and_then( From 85ef0170c4c60b4678d291d3e66f42d299285301 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 11 Oct 2025 10:13:00 +0200 Subject: [PATCH 71/89] book: encourage the use of `clippy_utils::sym` Also, explain how new symbols can be added to this module in order to compare names. --- book/src/development/common_tools_writing_lints.md | 7 +++---- book/src/development/method_checking.md | 11 +++++++---- book/src/development/trait_checking.md | 4 ++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/book/src/development/common_tools_writing_lints.md b/book/src/development/common_tools_writing_lints.md index 7fde4cb408a5e..b5958f802e384 100644 --- a/book/src/development/common_tools_writing_lints.md +++ b/book/src/development/common_tools_writing_lints.md @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for MyStructLint { // Check our expr is calling a method if let hir::ExprKind::MethodCall(path, _, _self_arg, ..) = &expr.kind // Check the name of this method is `some_method` - && path.ident.name.as_str() == "some_method" + && path.ident.name == sym::some_method // Optionally, check the type of the self argument. // - See "Checking for a specific type" { @@ -85,9 +85,8 @@ to check for. All of these methods only check for the base type, generic arguments have to be checked separately. ```rust -use clippy_utils::paths; +use clippy_utils::{paths, sym}; use clippy_utils::res::MaybeDef; -use rustc_span::symbol::sym; use rustc_hir::LangItem; impl LateLintPass<'_> for MyStructLint { @@ -123,8 +122,8 @@ There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither. ```rust +use clippy_utils::sym; use clippy_utils::ty::implements_trait; -use rustc_span::symbol::sym; impl LateLintPass<'_> for MyStructLint { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { diff --git a/book/src/development/method_checking.md b/book/src/development/method_checking.md index 7819a477f608c..cca6d6ae7bf34 100644 --- a/book/src/development/method_checking.md +++ b/book/src/development/method_checking.md @@ -15,15 +15,15 @@ the [`ExprKind`] that we can access from `expr.kind`: ```rust use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; -use rustc_span::sym; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::sym; impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { // Check our expr is calling a method with pattern matching if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind // Check if the name of this method is `our_fancy_method` - && path.ident.name.as_str() == "our_fancy_method" + && path.ident.name == sym::our_fancy_method // We can check the type of the self argument whenever necessary. // (It's necessary if we want to check that method is specifically belonging to a specific trait, // for example, a `map` method could belong to user-defined trait instead of to `Iterator`) @@ -41,6 +41,10 @@ information on the pattern matching. As mentioned in [Define Lints](defining_lints.md#lint-types), the `methods` lint type is full of pattern matching with `MethodCall` in case the reader wishes to explore more. +New symbols such as `our_fancy_method` need to be added to the `clippy_utils::sym` module. +This module extends the list of symbols already provided by the compiler crates +in `rustc_span::sym`. + ## Checking if a `impl` block implements a method While sometimes we want to check whether a method is being called or not, other @@ -56,11 +60,10 @@ Let us take a look at how we might check for the implementation of `our_fancy_method` on a type: ```rust +use clippy_utils::{return_ty, sym}; use clippy_utils::res::MaybeDef; -use clippy_utils::return_ty; use rustc_hir::{ImplItem, ImplItemKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_span::symbol::sym; impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { diff --git a/book/src/development/trait_checking.md b/book/src/development/trait_checking.md index c6f6f6bd2f99e..d01bb723da619 100644 --- a/book/src/development/trait_checking.md +++ b/book/src/development/trait_checking.md @@ -17,10 +17,10 @@ providing the `LateContext` (`cx`), our expression at hand, and the symbol of the trait in question: ```rust +use clippy_utils::sym; use clippy_utils::ty::implements_trait; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; -use rustc_span::symbol::sym; impl LateLintPass<'_> for CheckIteratorTraitLint { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { @@ -124,8 +124,8 @@ The following code demonstrates how to do this: ```rust use rustc_middle::ty::Ty; +use clippy_utils::sym; 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 From 9027625e1457ad9c97d14ff579cc3bb8743c5971 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 11 Oct 2025 10:16:52 +0200 Subject: [PATCH 72/89] book: use `Type::method` instead of `Type.method` to refer to methods --- 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 ed547130b358e..63a96dc373f31 100644 --- a/book/src/development/macro_expansions.md +++ b/book/src/development/macro_expansions.md @@ -37,7 +37,7 @@ before emitting suggestions to the end user to avoid false positives. Several functions are available for working with macros. -### The `Span.from_expansion` method +### The `Span::from_expansion` method We could utilize a `span`'s [`from_expansion`] method, which detects if the `span` is from a macro expansion / desugaring. @@ -50,7 +50,7 @@ if expr.span.from_expansion() { } ``` -### `Span.ctxt` method +### `Span::ctxt` method 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 From 1ffd092a8075bce16ab13efc8e9a6c700a642621 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 11 Oct 2025 10:17:26 +0200 Subject: [PATCH 73/89] book: import `implements_trait` from `clippy_utils::ty` --- book/src/development/trait_checking.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/book/src/development/trait_checking.md b/book/src/development/trait_checking.md index d01bb723da619..714607ef25e5f 100644 --- a/book/src/development/trait_checking.md +++ b/book/src/development/trait_checking.md @@ -53,7 +53,7 @@ For instance, if we want to examine whether an expression `expr` implements we can check that the `Ty` of the `expr` implements the trait: ```rust -use clippy_utils::implements_trait; +use clippy_utils::ty::implements_trait; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; @@ -79,7 +79,8 @@ If neither diagnostic item nor a language item is available, we can use Below, we check if the given `expr` implements [`core::iter::Step`](https://doc.rust-lang.org/std/iter/trait.Step.html): ```rust -use clippy_utils::{implements_trait, paths}; +use clippy_utils::paths; +use clippy_utils::ty::implements_trait; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; From bb5b5bce9985bebc9a9a2cf618a8551dc504a1b4 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 7 Oct 2025 00:47:34 +0200 Subject: [PATCH 74/89] feat(zero_repeat_side_effects): put the suggestion on two lines --- clippy_lints/src/zero_repeat_side_effects.rs | 18 +++++-- tests/ui/zero_repeat_side_effects.fixed | 43 +++++++++++---- tests/ui/zero_repeat_side_effects.stderr | 54 +++++++++++-------- ...ro_repeat_side_effects_never_pattern.fixed | 3 +- ...o_repeat_side_effects_never_pattern.stderr | 4 +- 5 files changed, 82 insertions(+), 40 deletions(-) diff --git a/clippy_lints/src/zero_repeat_side_effects.rs b/clippy_lints/src/zero_repeat_side_effects.rs index db54ec023077a..d6b916ffef9d3 100644 --- a/clippy_lints/src/zero_repeat_side_effects.rs +++ b/clippy_lints/src/zero_repeat_side_effects.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, snippet_indent}; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -77,24 +77,34 @@ fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: let return_type = cx.typeck_results().expr_ty(expr); let inner_expr = snippet(cx, inner_expr.span.source_callsite(), ".."); + let indent = snippet_indent(cx, expr.span).unwrap_or_default(); let vec = if is_vec { "vec!" } else { "" }; let (span, sugg) = match parent_hir_node { Node::LetStmt(l) => ( l.span, format!( - "{inner_expr}; let {var_name}: {return_type} = {vec}[];", + "{inner_expr};\n{indent}let {var_name}: {return_type} = {vec}[];", var_name = snippet(cx, l.pat.span.source_callsite(), "..") ), ), Node::Expr(x) if let ExprKind::Assign(l, _, _) = x.kind => ( x.span, format!( - "{inner_expr}; {var_name} = {vec}[] as {return_type}", + "{inner_expr};\n{indent}{var_name} = {vec}[] as {return_type}", var_name = snippet(cx, l.span.source_callsite(), "..") ), ), - _ => (expr.span, format!("{{ {inner_expr}; {vec}[] as {return_type} }}")), + _ => ( + expr.span, + format!( + "\ +{{ +{indent} {inner_expr}; +{indent} {vec}[] as {return_type} +{indent}}}" + ), + ), }; let span = span.source_callsite(); span_lint_and_then( diff --git a/tests/ui/zero_repeat_side_effects.fixed b/tests/ui/zero_repeat_side_effects.fixed index e6c451ce7399e..231ed85ee635c 100644 --- a/tests/ui/zero_repeat_side_effects.fixed +++ b/tests/ui/zero_repeat_side_effects.fixed @@ -13,36 +13,51 @@ fn main() { // should trigger // on arrays - f(); let a: [i32; 0] = []; + f(); + let a: [i32; 0] = []; //~^ zero_repeat_side_effects let mut b; - f(); b = [] as [i32; 0]; + f(); + b = [] as [i32; 0]; //~^ zero_repeat_side_effects // on vecs // vecs dont support inferring value of consts - f(); let c: std::vec::Vec = vec![]; + f(); + let c: std::vec::Vec = vec![]; //~^ zero_repeat_side_effects let d; - f(); d = vec![] as std::vec::Vec; + f(); + d = vec![] as std::vec::Vec; //~^ zero_repeat_side_effects // for macros - println!("side effect"); let e: [(); 0] = []; + println!("side effect"); + let e: [(); 0] = []; //~^ zero_repeat_side_effects // for nested calls - { f() }; let g: [i32; 0] = []; + { f() }; + let g: [i32; 0] = []; //~^ zero_repeat_side_effects // as function param - drop({ f(); vec![] as std::vec::Vec }); + drop({ + f(); + vec![] as std::vec::Vec + }); //~^ zero_repeat_side_effects // when singled out/not part of assignment/local - { f(); vec![] as std::vec::Vec }; + { + f(); + vec![] as std::vec::Vec + }; //~^ zero_repeat_side_effects - { f(); [] as [i32; 0] }; + { + f(); + [] as [i32; 0] + }; //~^ zero_repeat_side_effects // should not trigger @@ -96,8 +111,14 @@ fn issue_14681() { foo(&[Some(0i64); 0]); foo(&[Some(Some(0i64)); 0]); - foo(&{ Some(f()); [] as [std::option::Option; 0] }); + foo(&{ + Some(f()); + [] as [std::option::Option; 0] + }); //~^ zero_repeat_side_effects - foo(&{ Some(Some(S::new())); [] as [std::option::Option>; 0] }); + foo(&{ + Some(Some(S::new())); + [] as [std::option::Option>; 0] + }); //~^ zero_repeat_side_effects } diff --git a/tests/ui/zero_repeat_side_effects.stderr b/tests/ui/zero_repeat_side_effects.stderr index 771b71c686ae4..3f43f15da4a03 100644 --- a/tests/ui/zero_repeat_side_effects.stderr +++ b/tests/ui/zero_repeat_side_effects.stderr @@ -8,8 +8,8 @@ LL | let a = [f(); 0]; = help: to override `-D warnings` add `#[allow(clippy::zero_repeat_side_effects)]` help: consider performing the side effect separately | -LL - let a = [f(); 0]; -LL + f(); let a: [i32; 0] = []; +LL ~ f(); +LL + let a: [i32; 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer @@ -20,8 +20,8 @@ LL | b = [f(); 0]; | help: consider performing the side effect separately | -LL - b = [f(); 0]; -LL + f(); b = [] as [i32; 0]; +LL ~ f(); +LL ~ b = [] as [i32; 0]; | error: expression with side effects as the initial value in a zero-sized array initializer @@ -32,8 +32,8 @@ LL | let c = vec![f(); 0]; | help: consider performing the side effect separately | -LL - let c = vec![f(); 0]; -LL + f(); let c: std::vec::Vec = vec![]; +LL ~ f(); +LL + let c: std::vec::Vec = vec![]; | error: expression with side effects as the initial value in a zero-sized array initializer @@ -44,8 +44,8 @@ LL | d = vec![f(); 0]; | help: consider performing the side effect separately | -LL - d = vec![f(); 0]; -LL + f(); d = vec![] as std::vec::Vec; +LL ~ f(); +LL ~ d = vec![] as std::vec::Vec; | error: expression with side effects as the initial value in a zero-sized array initializer @@ -56,8 +56,8 @@ LL | let e = [println!("side effect"); 0]; | help: consider performing the side effect separately | -LL - let e = [println!("side effect"); 0]; -LL + println!("side effect"); let e: [(); 0] = []; +LL ~ println!("side effect"); +LL + let e: [(); 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer @@ -68,8 +68,8 @@ LL | let g = [{ f() }; 0]; | help: consider performing the side effect separately | -LL - let g = [{ f() }; 0]; -LL + { f() }; let g: [i32; 0] = []; +LL ~ { f() }; +LL + let g: [i32; 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer @@ -80,8 +80,10 @@ LL | drop(vec![f(); 0]); | help: consider performing the side effect separately | -LL - drop(vec![f(); 0]); -LL + drop({ f(); vec![] as std::vec::Vec }); +LL ~ drop({ +LL + f(); +LL + vec![] as std::vec::Vec +LL ~ }); | error: expression with side effects as the initial value in a zero-sized array initializer @@ -92,8 +94,10 @@ LL | vec![f(); 0]; | help: consider performing the side effect separately | -LL - vec![f(); 0]; -LL + { f(); vec![] as std::vec::Vec }; +LL ~ { +LL + f(); +LL + vec![] as std::vec::Vec +LL ~ }; | error: expression with side effects as the initial value in a zero-sized array initializer @@ -104,8 +108,10 @@ LL | [f(); 0]; | help: consider performing the side effect separately | -LL - [f(); 0]; -LL + { f(); [] as [i32; 0] }; +LL ~ { +LL + f(); +LL + [] as [i32; 0] +LL ~ }; | error: expression with side effects as the initial value in a zero-sized array initializer @@ -116,8 +122,10 @@ LL | foo(&[Some(f()); 0]); | help: consider performing the side effect separately | -LL - foo(&[Some(f()); 0]); -LL + foo(&{ Some(f()); [] as [std::option::Option; 0] }); +LL ~ foo(&{ +LL + Some(f()); +LL + [] as [std::option::Option; 0] +LL ~ }); | error: expression with side effects as the initial value in a zero-sized array initializer @@ -128,8 +136,10 @@ LL | foo(&[Some(Some(S::new())); 0]); | help: consider performing the side effect separately | -LL - foo(&[Some(Some(S::new())); 0]); -LL + foo(&{ Some(Some(S::new())); [] as [std::option::Option>; 0] }); +LL ~ foo(&{ +LL + Some(Some(S::new())); +LL + [] as [std::option::Option>; 0] +LL ~ }); | error: aborting due to 11 previous errors diff --git a/tests/ui/zero_repeat_side_effects_never_pattern.fixed b/tests/ui/zero_repeat_side_effects_never_pattern.fixed index 021265dac9844..3d037516f75c8 100644 --- a/tests/ui/zero_repeat_side_effects_never_pattern.fixed +++ b/tests/ui/zero_repeat_side_effects_never_pattern.fixed @@ -4,6 +4,7 @@ fn issue_14998() { // nameable type thanks to `never_type` being enabled, suggest - panic!(); let _data: [!; 0] = []; + panic!(); + let _data: [!; 0] = []; //~^ zero_repeat_side_effects } diff --git a/tests/ui/zero_repeat_side_effects_never_pattern.stderr b/tests/ui/zero_repeat_side_effects_never_pattern.stderr index b3d3d2f88f541..280955740cc4c 100644 --- a/tests/ui/zero_repeat_side_effects_never_pattern.stderr +++ b/tests/ui/zero_repeat_side_effects_never_pattern.stderr @@ -8,8 +8,8 @@ LL | let _data = [panic!(); 0]; = help: to override `-D warnings` add `#[allow(clippy::zero_repeat_side_effects)]` help: consider performing the side effect separately | -LL - let _data = [panic!(); 0]; -LL + panic!(); let _data: [!; 0] = []; +LL ~ panic!(); +LL + let _data: [!; 0] = []; | error: aborting due to 1 previous error From 6b076ca80bd62265229c9281ecd0fdfd4b241f1a Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 6 Oct 2025 18:24:57 +0200 Subject: [PATCH 75/89] feat(zero_repeat_side_effects): don't suggest unnecessary braces around stmts --- clippy_lints/src/zero_repeat_side_effects.rs | 2 ++ tests/ui/zero_repeat_side_effects.fixed | 13 +++----- tests/ui/zero_repeat_side_effects.rs | 1 + tests/ui/zero_repeat_side_effects.stderr | 34 +++++++++----------- 4 files changed, 23 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/zero_repeat_side_effects.rs b/clippy_lints/src/zero_repeat_side_effects.rs index d6b916ffef9d3..a8351690068df 100644 --- a/clippy_lints/src/zero_repeat_side_effects.rs +++ b/clippy_lints/src/zero_repeat_side_effects.rs @@ -95,6 +95,8 @@ fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: var_name = snippet(cx, l.span.source_callsite(), "..") ), ), + // NOTE: don't use the stmt span to avoid touching the trailing semicolon + Node::Stmt(_) => (expr.span, format!("{inner_expr};\n{indent}{vec}[] as {return_type}")), _ => ( expr.span, format!( diff --git a/tests/ui/zero_repeat_side_effects.fixed b/tests/ui/zero_repeat_side_effects.fixed index 231ed85ee635c..b5fca36f3f089 100644 --- a/tests/ui/zero_repeat_side_effects.fixed +++ b/tests/ui/zero_repeat_side_effects.fixed @@ -1,5 +1,6 @@ #![warn(clippy::zero_repeat_side_effects)] #![expect(clippy::unnecessary_operation, clippy::useless_vec, clippy::needless_late_init)] +#![allow(clippy::no_effect)] // only fires _after_ the fix fn f() -> i32 { println!("side effect"); @@ -49,15 +50,11 @@ fn main() { //~^ zero_repeat_side_effects // when singled out/not part of assignment/local - { - f(); - vec![] as std::vec::Vec - }; + f(); + vec![] as std::vec::Vec; //~^ zero_repeat_side_effects - { - f(); - [] as [i32; 0] - }; + f(); + [] as [i32; 0]; //~^ zero_repeat_side_effects // should not trigger diff --git a/tests/ui/zero_repeat_side_effects.rs b/tests/ui/zero_repeat_side_effects.rs index f8a497976aa43..ea043d21638cc 100644 --- a/tests/ui/zero_repeat_side_effects.rs +++ b/tests/ui/zero_repeat_side_effects.rs @@ -1,5 +1,6 @@ #![warn(clippy::zero_repeat_side_effects)] #![expect(clippy::unnecessary_operation, clippy::useless_vec, clippy::needless_late_init)] +#![allow(clippy::no_effect)] // only fires _after_ the fix fn f() -> i32 { println!("side effect"); diff --git a/tests/ui/zero_repeat_side_effects.stderr b/tests/ui/zero_repeat_side_effects.stderr index 3f43f15da4a03..49e850d035343 100644 --- a/tests/ui/zero_repeat_side_effects.stderr +++ b/tests/ui/zero_repeat_side_effects.stderr @@ -1,5 +1,5 @@ error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:16:5 + --> tests/ui/zero_repeat_side_effects.rs:17:5 | LL | let a = [f(); 0]; | ^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + let a: [i32; 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:19:5 + --> tests/ui/zero_repeat_side_effects.rs:20:5 | LL | b = [f(); 0]; | ^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL ~ b = [] as [i32; 0]; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:24:5 + --> tests/ui/zero_repeat_side_effects.rs:25:5 | LL | let c = vec![f(); 0]; | ^^^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL + let c: std::vec::Vec = vec![]; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:27:5 + --> tests/ui/zero_repeat_side_effects.rs:28:5 | LL | d = vec![f(); 0]; | ^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL ~ d = vec![] as std::vec::Vec; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:31:5 + --> tests/ui/zero_repeat_side_effects.rs:32:5 | LL | let e = [println!("side effect"); 0]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL + let e: [(); 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:35:5 + --> tests/ui/zero_repeat_side_effects.rs:36:5 | LL | let g = [{ f() }; 0]; | ^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL + let g: [i32; 0] = []; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:39:10 + --> tests/ui/zero_repeat_side_effects.rs:40:10 | LL | drop(vec![f(); 0]); | ^^^^^^^^^^^^ @@ -87,35 +87,31 @@ LL ~ }); | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:43:5 + --> tests/ui/zero_repeat_side_effects.rs:44:5 | LL | vec![f(); 0]; | ^^^^^^^^^^^^ | help: consider performing the side effect separately | -LL ~ { -LL + f(); -LL + vec![] as std::vec::Vec -LL ~ }; +LL ~ f(); +LL ~ vec![] as std::vec::Vec; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:45:5 + --> tests/ui/zero_repeat_side_effects.rs:46:5 | LL | [f(); 0]; | ^^^^^^^^ | help: consider performing the side effect separately | -LL ~ { -LL + f(); -LL + [] as [i32; 0] -LL ~ }; +LL ~ f(); +LL ~ [] as [i32; 0]; | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:99:10 + --> tests/ui/zero_repeat_side_effects.rs:100:10 | LL | foo(&[Some(f()); 0]); | ^^^^^^^^^^^^^^ @@ -129,7 +125,7 @@ LL ~ }); | error: expression with side effects as the initial value in a zero-sized array initializer - --> tests/ui/zero_repeat_side_effects.rs:101:10 + --> tests/ui/zero_repeat_side_effects.rs:102:10 | LL | foo(&[Some(Some(S::new())); 0]); | ^^^^^^^^^^^^^^^^^^^^^^^^^ From 85490d1845d67c026d5f09bef4b796b7b6c8f725 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 7 Sep 2025 21:56:03 +0200 Subject: [PATCH 76/89] clean-up --- clippy_lints/src/mutex_atomic.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index a93665ef3e9d6..306f9dd1b8a7f 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -93,18 +93,17 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { let ty = cx.typeck_results().expr_ty(expr); if let ty::Adt(_, subst) = ty.kind() && ty.is_diag_item(cx, sym::Mutex) + && let mutex_param = subst.type_at(0) + && let Some(atomic_name) = get_atomic_name(mutex_param) { - let mutex_param = subst.type_at(0); - if let Some(atomic_name) = get_atomic_name(mutex_param) { - let msg = format!( - "consider using an `{atomic_name}` instead of a `Mutex` here; if you just want the locking \ - behavior and not the internal type, consider using `Mutex<()>`" - ); - match *mutex_param.kind() { - ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), - ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), - _ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg), - } + let msg = format!( + "consider using an `{atomic_name}` instead of a `Mutex` here; if you just want the locking \ + behavior and not the internal type, consider using `Mutex<()>`" + ); + match *mutex_param.kind() { + ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), + ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), + _ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg), } } } From 3ae047ee04671f43bac3ee80577618faa0e132fe Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 7 Sep 2025 21:47:25 +0200 Subject: [PATCH 77/89] restructure messages - The main message should point out what's wrong, not directly suggest a solution. - The second part of the message is a separate advice, so it should be emitted separately. --- clippy_lints/src/mutex_atomic.rs | 20 +++++++----- tests/ui/mutex_atomic.stderr | 53 +++++++++++++++++++++++++------- 2 files changed, 54 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 306f9dd1b8a7f..7ff8729fe0605 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -1,5 +1,6 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; +use rustc_errors::Diag; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, IntTy, Ty, UintTy}; @@ -96,14 +97,17 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { && let mutex_param = subst.type_at(0) && let Some(atomic_name) = get_atomic_name(mutex_param) { - let msg = format!( - "consider using an `{atomic_name}` instead of a `Mutex` here; if you just want the locking \ - behavior and not the internal type, consider using `Mutex<()>`" - ); + let msg = "using a `Mutex` where an atomic would do"; + let diag = |diag: &mut Diag<'_, _>| { + diag.help(format!("consider using an `{atomic_name}` instead")); + diag.help( + "if you just want the locking behavior and not the internal type, consider using `Mutex<()>`", + ); + }; match *mutex_param.kind() { - ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), - ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), - _ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg), + ty::Uint(t) if t != UintTy::Usize => span_lint_and_then(cx, MUTEX_INTEGER, expr.span, msg, diag), + ty::Int(t) if t != IntTy::Isize => span_lint_and_then(cx, MUTEX_INTEGER, expr.span, msg, diag), + _ => span_lint_and_then(cx, MUTEX_ATOMIC, expr.span, msg, diag), } } } diff --git a/tests/ui/mutex_atomic.stderr b/tests/ui/mutex_atomic.stderr index a6d5d60fbf05b..b328461ff0ced 100644 --- a/tests/ui/mutex_atomic.stderr +++ b/tests/ui/mutex_atomic.stderr @@ -1,74 +1,105 @@ -error: consider using an `AtomicBool` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:7:5 | LL | Mutex::new(true); | ^^^^^^^^^^^^^^^^ | + = help: consider using an `AtomicBool` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` = note: `-D clippy::mutex-atomic` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mutex_atomic)]` -error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:10:5 | LL | Mutex::new(5usize); | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using an `AtomicUsize` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:13:5 | LL | Mutex::new(9isize); | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using an `AtomicIsize` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:17:5 | LL | Mutex::new(&x as *const u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an `AtomicPtr` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:20:5 | LL | Mutex::new(&mut x as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an `AtomicPtr` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicU32` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:23:5 | LL | Mutex::new(0u32); | ^^^^^^^^^^^^^^^^ | + = help: consider using an `AtomicU32` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` = note: `-D clippy::mutex-integer` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mutex_integer)]` -error: consider using an `AtomicI32` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:26:5 | LL | Mutex::new(0i32); | ^^^^^^^^^^^^^^^^ + | + = help: consider using an `AtomicI32` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicU8` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:30:5 | LL | Mutex::new(0u8); | ^^^^^^^^^^^^^^^ + | + = help: consider using an `AtomicU8` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicI16` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:33:5 | LL | Mutex::new(0i16); | ^^^^^^^^^^^^^^^^ + | + = help: consider using an `AtomicI16` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicI8` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:36:25 | LL | let _x: Mutex = Mutex::new(0); | ^^^^^^^^^^^^^ + | + = help: consider using an `AtomicI8` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: consider using an `AtomicI64` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:40:5 | LL | Mutex::new(X); | ^^^^^^^^^^^^^ + | + = help: consider using an `AtomicI64` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: aborting due to 11 previous errors From 38ac3d041c03f6898ea28fddad245c3e29645de3 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 7 Sep 2025 22:45:53 +0200 Subject: [PATCH 78/89] only lint on definitions, not use --- clippy_lints/src/mutex_atomic.rs | 56 +++++++++++++------- tests/ui/mutex_atomic.rs | 43 ++++++++++----- tests/ui/mutex_atomic.stderr | 91 +++++++++++++++++++++----------- 3 files changed, 126 insertions(+), 64 deletions(-) diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 7ff8729fe0605..d096d965ed322 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; +use clippy_utils::ty::ty_from_hir_ty; use rustc_errors::Diag; -use rustc_hir::Expr; +use rustc_hir::{Expr, Item, ItemKind, LetStmt}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, IntTy, Ty, UintTy}; use rustc_session::declare_lint_pass; @@ -89,26 +90,43 @@ declare_clippy_lint! { declare_lint_pass!(Mutex => [MUTEX_ATOMIC, MUTEX_INTEGER]); +// NOTE: we don't use `check_expr` because that would make us lint every _use_ of such mutexes, not +// just their definitions impl<'tcx> LateLintPass<'tcx> for Mutex { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(_, subst) = ty.kind() - && ty.is_diag_item(cx, sym::Mutex) - && let mutex_param = subst.type_at(0) - && let Some(atomic_name) = get_atomic_name(mutex_param) + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if !item.span.from_expansion() + && let ItemKind::Static(_, _, ty, body_id) = item.kind { - let msg = "using a `Mutex` where an atomic would do"; - let diag = |diag: &mut Diag<'_, _>| { - diag.help(format!("consider using an `{atomic_name}` instead")); - diag.help( - "if you just want the locking behavior and not the internal type, consider using `Mutex<()>`", - ); - }; - match *mutex_param.kind() { - ty::Uint(t) if t != UintTy::Usize => span_lint_and_then(cx, MUTEX_INTEGER, expr.span, msg, diag), - ty::Int(t) if t != IntTy::Isize => span_lint_and_then(cx, MUTEX_INTEGER, expr.span, msg, diag), - _ => span_lint_and_then(cx, MUTEX_ATOMIC, expr.span, msg, diag), - } + let body = cx.tcx.hir_body(body_id); + let mid_ty = ty_from_hir_ty(cx, ty); + check_expr(cx, body.value.peel_blocks(), mid_ty); + } + } + fn check_local(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx LetStmt<'_>) { + if !stmt.span.from_expansion() + && let Some(init) = stmt.init + { + let mid_ty = cx.typeck_results().expr_ty(init); + check_expr(cx, init.peel_blocks(), mid_ty); + } + } +} + +fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, ty: Ty<'tcx>) { + if let ty::Adt(_, subst) = ty.kind() + && ty.is_diag_item(cx, sym::Mutex) + && let mutex_param = subst.type_at(0) + && let Some(atomic_name) = get_atomic_name(mutex_param) + { + let msg = "using a `Mutex` where an atomic would do"; + let diag = |diag: &mut Diag<'_, _>| { + diag.help(format!("consider using an `{atomic_name}` instead")); + diag.help("if you just want the locking behavior and not the internal type, consider using `Mutex<()>`"); + }; + match *mutex_param.kind() { + ty::Uint(t) if t != UintTy::Usize => span_lint_and_then(cx, MUTEX_INTEGER, expr.span, msg, diag), + ty::Int(t) if t != IntTy::Isize => span_lint_and_then(cx, MUTEX_INTEGER, expr.span, msg, diag), + _ => span_lint_and_then(cx, MUTEX_ATOMIC, expr.span, msg, diag), } } } diff --git a/tests/ui/mutex_atomic.rs b/tests/ui/mutex_atomic.rs index 7db5c9f274f6e..cc53cc3136c63 100644 --- a/tests/ui/mutex_atomic.rs +++ b/tests/ui/mutex_atomic.rs @@ -2,47 +2,64 @@ #![warn(clippy::mutex_atomic)] #![allow(clippy::borrow_as_ptr)] +use std::sync::Mutex; + fn main() { - use std::sync::Mutex; - Mutex::new(true); + let _ = Mutex::new(true); //~^ mutex_atomic - Mutex::new(5usize); + let _ = Mutex::new(5usize); //~^ mutex_atomic - Mutex::new(9isize); + let _ = Mutex::new(9isize); //~^ mutex_atomic let mut x = 4u32; - Mutex::new(&x as *const u32); + let _ = Mutex::new(&x as *const u32); //~^ mutex_atomic - Mutex::new(&mut x as *mut u32); + let _ = Mutex::new(&mut x as *mut u32); //~^ mutex_atomic - Mutex::new(0u32); + let _ = Mutex::new(0u32); //~^ mutex_integer - Mutex::new(0i32); + let _ = Mutex::new(0i32); //~^ mutex_integer - Mutex::new(0f32); // there are no float atomics, so this should not lint - Mutex::new(0u8); + let _ = Mutex::new(0f32); // there are no float atomics, so this should not lint + let _ = Mutex::new(0u8); //~^ mutex_integer - Mutex::new(0i16); + let _ = Mutex::new(0i16); //~^ mutex_integer let _x: Mutex = Mutex::new(0); //~^ mutex_integer const X: i64 = 0; - Mutex::new(X); + let _ = Mutex::new(X); //~^ mutex_integer // there are no 128 atomics, so these two should not lint { - Mutex::new(0u128); + let _ = Mutex::new(0u128); let _x: Mutex = Mutex::new(0); } } + +static MTX: Mutex = Mutex::new(0); +//~^ mutex_integer + +// don't lint on _use_, only declaration +fn issue13378() { + let mut guard = MTX.lock().unwrap(); + *guard += 1; + + let mtx = Mutex::new(0); + //~^ mutex_integer + // This will still lint, since we're reassigning the mutex to a variable -- oh well. + // But realistically something like this won't really come up. + let reassigned = mtx; + //~^ mutex_integer +} diff --git a/tests/ui/mutex_atomic.stderr b/tests/ui/mutex_atomic.stderr index b328461ff0ced..839b641c574ad 100644 --- a/tests/ui/mutex_atomic.stderr +++ b/tests/ui/mutex_atomic.stderr @@ -1,8 +1,8 @@ error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:7:5 + --> tests/ui/mutex_atomic.rs:8:13 | -LL | Mutex::new(true); - | ^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(true); + | ^^^^^^^^^^^^^^^^ | = help: consider using an `AtomicBool` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` @@ -10,46 +10,46 @@ LL | Mutex::new(true); = help: to override `-D warnings` add `#[allow(clippy::mutex_atomic)]` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:10:5 + --> tests/ui/mutex_atomic.rs:11:13 | -LL | Mutex::new(5usize); - | ^^^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(5usize); + | ^^^^^^^^^^^^^^^^^^ | = help: consider using an `AtomicUsize` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:13:5 + --> tests/ui/mutex_atomic.rs:14:13 | -LL | Mutex::new(9isize); - | ^^^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(9isize); + | ^^^^^^^^^^^^^^^^^^ | = help: consider using an `AtomicIsize` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:17:5 + --> tests/ui/mutex_atomic.rs:18:13 | -LL | Mutex::new(&x as *const u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(&x as *const u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using an `AtomicPtr` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:20:5 + --> tests/ui/mutex_atomic.rs:21:13 | -LL | Mutex::new(&mut x as *mut u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(&mut x as *mut u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using an `AtomicPtr` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:23:5 + --> tests/ui/mutex_atomic.rs:24:13 | -LL | Mutex::new(0u32); - | ^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(0u32); + | ^^^^^^^^^^^^^^^^ | = help: consider using an `AtomicU32` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` @@ -57,34 +57,34 @@ LL | Mutex::new(0u32); = help: to override `-D warnings` add `#[allow(clippy::mutex_integer)]` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:26:5 + --> tests/ui/mutex_atomic.rs:27:13 | -LL | Mutex::new(0i32); - | ^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(0i32); + | ^^^^^^^^^^^^^^^^ | = help: consider using an `AtomicI32` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:30:5 + --> tests/ui/mutex_atomic.rs:31:13 | -LL | Mutex::new(0u8); - | ^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(0u8); + | ^^^^^^^^^^^^^^^ | = help: consider using an `AtomicU8` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:33:5 + --> tests/ui/mutex_atomic.rs:34:13 | -LL | Mutex::new(0i16); - | ^^^^^^^^^^^^^^^^ +LL | let _ = Mutex::new(0i16); + | ^^^^^^^^^^^^^^^^ | = help: consider using an `AtomicI16` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:36:25 + --> tests/ui/mutex_atomic.rs:37:25 | LL | let _x: Mutex = Mutex::new(0); | ^^^^^^^^^^^^^ @@ -93,13 +93,40 @@ LL | let _x: Mutex = Mutex::new(0); = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:40:5 + --> tests/ui/mutex_atomic.rs:41:13 | -LL | Mutex::new(X); - | ^^^^^^^^^^^^^ +LL | let _ = Mutex::new(X); + | ^^^^^^^^^^^^^ | = help: consider using an `AtomicI64` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: aborting due to 11 previous errors +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:51:26 + | +LL | static MTX: Mutex = Mutex::new(0); + | ^^^^^^^^^^^^^ + | + = help: consider using an `AtomicU32` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` + +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:59:15 + | +LL | let mtx = Mutex::new(0); + | ^^^^^^^^^^^^^ + | + = help: consider using an `AtomicI32` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` + +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:63:22 + | +LL | let reassigned = mtx; + | ^^^ + | + = help: consider using an `AtomicI32` instead + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` + +error: aborting due to 14 previous errors From 99ce6391ddaa2470759443b7dad9ca097d6dc93c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 7 Sep 2025 23:37:53 +0200 Subject: [PATCH 79/89] suggest replacing `Mutex::new` with `AtomicX::new` --- clippy_lints/src/mutex_atomic.rs | 19 +++++++-- tests/ui/mutex_atomic.rs | 7 ++-- tests/ui/mutex_atomic.stderr | 69 +++++++++++++------------------- 3 files changed, 48 insertions(+), 47 deletions(-) diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index d096d965ed322..c92bad1393cab 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::ty_from_hir_ty; -use rustc_errors::Diag; -use rustc_hir::{Expr, Item, ItemKind, LetStmt}; +use rustc_errors::{Applicability, Diag}; +use rustc_hir::{Expr, ExprKind, Item, ItemKind, LetStmt, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, IntTy, Ty, UintTy}; use rustc_session::declare_lint_pass; @@ -120,7 +121,19 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, ty: Ty<'tcx>) { { let msg = "using a `Mutex` where an atomic would do"; let diag = |diag: &mut Diag<'_, _>| { - diag.help(format!("consider using an `{atomic_name}` instead")); + // if `expr = Mutex::new(arg)`, we can try emitting a suggestion + if let ExprKind::Call(qpath, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(_mutex, new)) = qpath.kind + && new.ident.name == sym::new + { + let mut applicability = Applicability::MaybeIncorrect; + let arg = Sugg::hir_with_applicability(cx, arg, "_", &mut applicability); + + let suggs = vec![(expr.span, format!("std::sync::atomic::{atomic_name}::new({arg})"))]; + diag.multipart_suggestion("try", suggs, applicability); + } else { + diag.help(format!("consider using an `{atomic_name}` instead")); + } diag.help("if you just want the locking behavior and not the internal type, consider using `Mutex<()>`"); }; match *mutex_param.kind() { diff --git a/tests/ui/mutex_atomic.rs b/tests/ui/mutex_atomic.rs index cc53cc3136c63..331c09ab1a71b 100644 --- a/tests/ui/mutex_atomic.rs +++ b/tests/ui/mutex_atomic.rs @@ -1,3 +1,4 @@ +//@no-rustfix #![warn(clippy::mutex_integer)] #![warn(clippy::mutex_atomic)] #![allow(clippy::borrow_as_ptr)] @@ -48,11 +49,11 @@ fn main() { } } -static MTX: Mutex = Mutex::new(0); -//~^ mutex_integer - // don't lint on _use_, only declaration fn issue13378() { + static MTX: Mutex = Mutex::new(0); + //~^ mutex_integer + let mut guard = MTX.lock().unwrap(); *guard += 1; diff --git a/tests/ui/mutex_atomic.stderr b/tests/ui/mutex_atomic.stderr index 839b641c574ad..9afbf72dc67d2 100644 --- a/tests/ui/mutex_atomic.stderr +++ b/tests/ui/mutex_atomic.stderr @@ -1,126 +1,113 @@ error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:8:13 + --> tests/ui/mutex_atomic.rs:9:13 | LL | let _ = Mutex::new(true); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicBool::new(true)` | - = help: consider using an `AtomicBool` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` = note: `-D clippy::mutex-atomic` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mutex_atomic)]` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:11:13 + --> tests/ui/mutex_atomic.rs:12:13 | LL | let _ = Mutex::new(5usize); - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicUsize::new(5usize)` | - = help: consider using an `AtomicUsize` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:14:13 + --> tests/ui/mutex_atomic.rs:15:13 | LL | let _ = Mutex::new(9isize); - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicIsize::new(9isize)` | - = help: consider using an `AtomicIsize` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:18:13 + --> tests/ui/mutex_atomic.rs:19:13 | LL | let _ = Mutex::new(&x as *const u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicPtr::new(&x as *const u32)` | - = help: consider using an `AtomicPtr` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:21:13 + --> tests/ui/mutex_atomic.rs:22:13 | LL | let _ = Mutex::new(&mut x as *mut u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicPtr::new(&mut x as *mut u32)` | - = help: consider using an `AtomicPtr` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:24:13 + --> tests/ui/mutex_atomic.rs:25:13 | LL | let _ = Mutex::new(0u32); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicU32::new(0u32)` | - = help: consider using an `AtomicU32` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` = note: `-D clippy::mutex-integer` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mutex_integer)]` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:27:13 + --> tests/ui/mutex_atomic.rs:28:13 | LL | let _ = Mutex::new(0i32); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI32::new(0i32)` | - = help: consider using an `AtomicI32` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:31:13 + --> tests/ui/mutex_atomic.rs:32:13 | LL | let _ = Mutex::new(0u8); - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicU8::new(0u8)` | - = help: consider using an `AtomicU8` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:34:13 + --> tests/ui/mutex_atomic.rs:35:13 | LL | let _ = Mutex::new(0i16); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI16::new(0i16)` | - = help: consider using an `AtomicI16` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:37:25 + --> tests/ui/mutex_atomic.rs:38:25 | LL | let _x: Mutex = Mutex::new(0); - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI8::new(0)` | - = help: consider using an `AtomicI8` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:41:13 + --> tests/ui/mutex_atomic.rs:42:13 | LL | let _ = Mutex::new(X); - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI64::new(X)` | - = help: consider using an `AtomicI64` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:51:26 + --> tests/ui/mutex_atomic.rs:54:30 | -LL | static MTX: Mutex = Mutex::new(0); - | ^^^^^^^^^^^^^ +LL | static MTX: Mutex = Mutex::new(0); + | ^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicU32::new(0)` | - = help: consider using an `AtomicU32` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:59:15 + --> tests/ui/mutex_atomic.rs:60:15 | LL | let mtx = Mutex::new(0); - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI32::new(0)` | - = help: consider using an `AtomicI32` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:63:22 + --> tests/ui/mutex_atomic.rs:64:22 | LL | let reassigned = mtx; | ^^^ From e5fd5714145bd167ac82d306219e8ae65c8f699e Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 8 Sep 2025 01:05:38 +0200 Subject: [PATCH 80/89] realize that a test case is incorrect `Mutex<*const _>` doesn't make a lot of sense (there can be no contention over a read-only reference), but `AtomicPtr::new(*const _)` straight up doesn't compile --- clippy_lints/src/mutex_atomic.rs | 4 +++- tests/ui/mutex_atomic.rs | 2 +- tests/ui/mutex_atomic.stderr | 10 +--------- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index c92bad1393cab..1cb6b901723f3 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -5,6 +5,7 @@ use clippy_utils::ty::ty_from_hir_ty; use rustc_errors::{Applicability, Diag}; use rustc_hir::{Expr, ExprKind, Item, ItemKind, LetStmt, QPath}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::Mutability; use rustc_middle::ty::{self, IntTy, Ty, UintTy}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -169,7 +170,8 @@ fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> { IntTy::I128 => None, } }, - ty::RawPtr(_, _) => Some("AtomicPtr"), + // `AtomicPtr` only accepts `*mut T` + ty::RawPtr(_, Mutability::Mut) => Some("AtomicPtr"), _ => None, } } diff --git a/tests/ui/mutex_atomic.rs b/tests/ui/mutex_atomic.rs index 331c09ab1a71b..b907c30818413 100644 --- a/tests/ui/mutex_atomic.rs +++ b/tests/ui/mutex_atomic.rs @@ -16,8 +16,8 @@ fn main() { //~^ mutex_atomic let mut x = 4u32; + // `AtomicPtr` only accepts `*mut T`, so this should not lint let _ = Mutex::new(&x as *const u32); - //~^ mutex_atomic let _ = Mutex::new(&mut x as *mut u32); //~^ mutex_atomic diff --git a/tests/ui/mutex_atomic.stderr b/tests/ui/mutex_atomic.stderr index 9afbf72dc67d2..9d8a7ccb33bd3 100644 --- a/tests/ui/mutex_atomic.stderr +++ b/tests/ui/mutex_atomic.stderr @@ -24,14 +24,6 @@ LL | let _ = Mutex::new(9isize); | = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:19:13 - | -LL | let _ = Mutex::new(&x as *const u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicPtr::new(&x as *const u32)` - | - = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` - error: using a `Mutex` where an atomic would do --> tests/ui/mutex_atomic.rs:22:13 | @@ -115,5 +107,5 @@ LL | let reassigned = mtx; = help: consider using an `AtomicI32` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: aborting due to 14 previous errors +error: aborting due to 13 previous errors From 778da589c6357c489ba1b6f3bb022359ca73435c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 8 Sep 2025 00:18:21 +0200 Subject: [PATCH 81/89] suggest adjusting the type ascription --- clippy_lints/src/mutex_atomic.rs | 43 ++++++++++++++--- tests/ui/mutex_atomic.fixed | 67 ++++++++++++++++++++++++++ tests/ui/mutex_atomic.rs | 9 ++-- tests/ui/mutex_atomic.stderr | 55 +++++++++++++++------ tests/ui/mutex_atomic_unfixable.rs | 13 +++++ tests/ui/mutex_atomic_unfixable.stderr | 17 +++++++ 6 files changed, 177 insertions(+), 27 deletions(-) create mode 100644 tests/ui/mutex_atomic.fixed create mode 100644 tests/ui/mutex_atomic_unfixable.rs create mode 100644 tests/ui/mutex_atomic_unfixable.stderr diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 1cb6b901723f3..2fef8404f824d 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; +use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::ty_from_hir_ty; use rustc_errors::{Applicability, Diag}; -use rustc_hir::{Expr, ExprKind, Item, ItemKind, LetStmt, QPath}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_hir::{self as hir, Expr, ExprKind, Item, ItemKind, LetStmt, QPath}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::mir::Mutability; use rustc_middle::ty::{self, IntTy, Ty, UintTy}; use rustc_session::declare_lint_pass; @@ -101,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { { let body = cx.tcx.hir_body(body_id); let mid_ty = ty_from_hir_ty(cx, ty); - check_expr(cx, body.value.peel_blocks(), mid_ty); + check_expr(cx, body.value.peel_blocks(), &TypeAscriptionKind::Required(ty), mid_ty); } } fn check_local(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx LetStmt<'_>) { @@ -109,12 +110,26 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { && let Some(init) = stmt.init { let mid_ty = cx.typeck_results().expr_ty(init); - check_expr(cx, init.peel_blocks(), mid_ty); + check_expr(cx, init.peel_blocks(), &TypeAscriptionKind::Optional(stmt.ty), mid_ty); } } } -fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, ty: Ty<'tcx>) { +/// Whether the type ascription `: Mutex` (which we'll suggest replacing with `AtomicX`) is +/// required +enum TypeAscriptionKind<'tcx> { + /// Yes; for us, this is the case for statics + Required(&'tcx hir::Ty<'tcx>), + /// No; the ascription might've been necessary in an expression like: + /// ```ignore + /// let mutex: Mutex = Mutex::new(0); + /// ``` + /// to specify the type of `0`, but since `AtomicX` already refers to a concrete type, we won't + /// need this ascription anymore. + Optional(Option<&'tcx hir::Ty<'tcx>>), +} + +fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, ty_ascription: &TypeAscriptionKind<'tcx>, ty: Ty<'tcx>) { if let ty::Adt(_, subst) = ty.kind() && ty.is_diag_item(cx, sym::Mutex) && let mutex_param = subst.type_at(0) @@ -129,8 +144,22 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, ty: Ty<'tcx>) { { let mut applicability = Applicability::MaybeIncorrect; let arg = Sugg::hir_with_applicability(cx, arg, "_", &mut applicability); - - let suggs = vec![(expr.span, format!("std::sync::atomic::{atomic_name}::new({arg})"))]; + let mut suggs = vec![(expr.span, format!("std::sync::atomic::{atomic_name}::new({arg})"))]; + match ty_ascription { + TypeAscriptionKind::Required(ty_ascription) => { + suggs.push((ty_ascription.span, format!("std::sync::atomic::{atomic_name}"))); + }, + TypeAscriptionKind::Optional(Some(ty_ascription)) => { + // See https://github.com/rust-lang/rust-clippy/pull/15386 for why this is + // required + let colon_ascription = (cx.sess().source_map()) + .span_extend_to_prev_char_before(ty_ascription.span, ':', true) + .with_leading_whitespace(cx) + .into_span(); + suggs.push((colon_ascription, String::new())); + }, + TypeAscriptionKind::Optional(None) => {}, // nothing to remove/replace + } diag.multipart_suggestion("try", suggs, applicability); } else { diag.help(format!("consider using an `{atomic_name}` instead")); diff --git a/tests/ui/mutex_atomic.fixed b/tests/ui/mutex_atomic.fixed new file mode 100644 index 0000000000000..e4218726019f6 --- /dev/null +++ b/tests/ui/mutex_atomic.fixed @@ -0,0 +1,67 @@ +#![warn(clippy::mutex_integer)] +#![warn(clippy::mutex_atomic)] +#![allow(clippy::borrow_as_ptr)] + +use std::sync::Mutex; + +fn main() { + let _ = std::sync::atomic::AtomicBool::new(true); + //~^ mutex_atomic + + let _ = std::sync::atomic::AtomicUsize::new(5usize); + //~^ mutex_atomic + + let _ = std::sync::atomic::AtomicIsize::new(9isize); + //~^ mutex_atomic + + let mut x = 4u32; + // `AtomicPtr` only accepts `*mut T`, so this should not lint + let _ = Mutex::new(&x as *const u32); + + let _ = std::sync::atomic::AtomicPtr::new(&mut x as *mut u32); + //~^ mutex_atomic + + let _ = std::sync::atomic::AtomicU32::new(0u32); + //~^ mutex_integer + + let _ = std::sync::atomic::AtomicI32::new(0i32); + //~^ mutex_integer + + let _ = Mutex::new(0f32); // there are no float atomics, so this should not lint + let _ = std::sync::atomic::AtomicU8::new(0u8); + //~^ mutex_integer + + let _ = std::sync::atomic::AtomicI16::new(0i16); + //~^ mutex_integer + + let _x = std::sync::atomic::AtomicI8::new(0); + //~^ mutex_integer + + const X: i64 = 0; + let _ = std::sync::atomic::AtomicI64::new(X); + //~^ mutex_integer + + // there are no 128 atomics, so these two should not lint + { + let _ = Mutex::new(0u128); + let _x: Mutex = Mutex::new(0); + } +} + +// don't lint on _use_, only declaration +fn issue13378() { + static MTX: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0); + //~^ mutex_integer + + let mtx = std::sync::atomic::AtomicI32::new(0); + //~^ mutex_integer + // This will still lint, since we're reassigning the mutex to a variable -- oh well. + // But realistically something like this won't really come up. + let reassigned = mtx; + //~^ mutex_integer + + // don't eat the `)` when removing the type ascription -- see + // https://github.com/rust-lang/rust-clippy/issues/15377 + let (funky_mtx) = std::sync::atomic::AtomicU64::new(0); + //~^ mutex_integer +} diff --git a/tests/ui/mutex_atomic.rs b/tests/ui/mutex_atomic.rs index b907c30818413..95f2b135903f7 100644 --- a/tests/ui/mutex_atomic.rs +++ b/tests/ui/mutex_atomic.rs @@ -1,4 +1,3 @@ -//@no-rustfix #![warn(clippy::mutex_integer)] #![warn(clippy::mutex_atomic)] #![allow(clippy::borrow_as_ptr)] @@ -54,13 +53,15 @@ fn issue13378() { static MTX: Mutex = Mutex::new(0); //~^ mutex_integer - let mut guard = MTX.lock().unwrap(); - *guard += 1; - let mtx = Mutex::new(0); //~^ mutex_integer // This will still lint, since we're reassigning the mutex to a variable -- oh well. // But realistically something like this won't really come up. let reassigned = mtx; //~^ mutex_integer + + // don't eat the `)` when removing the type ascription -- see + // https://github.com/rust-lang/rust-clippy/issues/15377 + let (funky_mtx): Mutex = Mutex::new(0); + //~^ mutex_integer } diff --git a/tests/ui/mutex_atomic.stderr b/tests/ui/mutex_atomic.stderr index 9d8a7ccb33bd3..0afc6d541deab 100644 --- a/tests/ui/mutex_atomic.stderr +++ b/tests/ui/mutex_atomic.stderr @@ -1,5 +1,5 @@ error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:9:13 + --> tests/ui/mutex_atomic.rs:8:13 | LL | let _ = Mutex::new(true); | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicBool::new(true)` @@ -9,7 +9,7 @@ LL | let _ = Mutex::new(true); = help: to override `-D warnings` add `#[allow(clippy::mutex_atomic)]` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:12:13 + --> tests/ui/mutex_atomic.rs:11:13 | LL | let _ = Mutex::new(5usize); | ^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicUsize::new(5usize)` @@ -17,7 +17,7 @@ LL | let _ = Mutex::new(5usize); = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:15:13 + --> tests/ui/mutex_atomic.rs:14:13 | LL | let _ = Mutex::new(9isize); | ^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicIsize::new(9isize)` @@ -25,7 +25,7 @@ LL | let _ = Mutex::new(9isize); = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:22:13 + --> tests/ui/mutex_atomic.rs:21:13 | LL | let _ = Mutex::new(&mut x as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicPtr::new(&mut x as *mut u32)` @@ -33,7 +33,7 @@ LL | let _ = Mutex::new(&mut x as *mut u32); = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:25:13 + --> tests/ui/mutex_atomic.rs:24:13 | LL | let _ = Mutex::new(0u32); | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicU32::new(0u32)` @@ -43,7 +43,7 @@ LL | let _ = Mutex::new(0u32); = help: to override `-D warnings` add `#[allow(clippy::mutex_integer)]` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:28:13 + --> tests/ui/mutex_atomic.rs:27:13 | LL | let _ = Mutex::new(0i32); | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI32::new(0i32)` @@ -51,7 +51,7 @@ LL | let _ = Mutex::new(0i32); = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:32:13 + --> tests/ui/mutex_atomic.rs:31:13 | LL | let _ = Mutex::new(0u8); | ^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicU8::new(0u8)` @@ -59,7 +59,7 @@ LL | let _ = Mutex::new(0u8); = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:35:13 + --> tests/ui/mutex_atomic.rs:34:13 | LL | let _ = Mutex::new(0i16); | ^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI16::new(0i16)` @@ -67,15 +67,20 @@ LL | let _ = Mutex::new(0i16); = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:38:25 + --> tests/ui/mutex_atomic.rs:37:25 | LL | let _x: Mutex = Mutex::new(0); - | ^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI8::new(0)` + | ^^^^^^^^^^^^^ | = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +help: try + | +LL - let _x: Mutex = Mutex::new(0); +LL + let _x = std::sync::atomic::AtomicI8::new(0); + | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:42:13 + --> tests/ui/mutex_atomic.rs:41:13 | LL | let _ = Mutex::new(X); | ^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI64::new(X)` @@ -83,15 +88,20 @@ LL | let _ = Mutex::new(X); = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:54:30 + --> tests/ui/mutex_atomic.rs:53:30 | LL | static MTX: Mutex = Mutex::new(0); - | ^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicU32::new(0)` + | ^^^^^^^^^^^^^ | = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +help: try + | +LL - static MTX: Mutex = Mutex::new(0); +LL + static MTX: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0); + | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:60:15 + --> tests/ui/mutex_atomic.rs:56:15 | LL | let mtx = Mutex::new(0); | ^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicI32::new(0)` @@ -99,7 +109,7 @@ LL | let mtx = Mutex::new(0); = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:64:22 + --> tests/ui/mutex_atomic.rs:60:22 | LL | let reassigned = mtx; | ^^^ @@ -107,5 +117,18 @@ LL | let reassigned = mtx; = help: consider using an `AtomicI32` instead = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` -error: aborting due to 13 previous errors +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:65:35 + | +LL | let (funky_mtx): Mutex = Mutex::new(0); + | ^^^^^^^^^^^^^ + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +help: try + | +LL - let (funky_mtx): Mutex = Mutex::new(0); +LL + let (funky_mtx) = std::sync::atomic::AtomicU64::new(0); + | + +error: aborting due to 14 previous errors diff --git a/tests/ui/mutex_atomic_unfixable.rs b/tests/ui/mutex_atomic_unfixable.rs new file mode 100644 index 0000000000000..0c04f48cf8a9e --- /dev/null +++ b/tests/ui/mutex_atomic_unfixable.rs @@ -0,0 +1,13 @@ +//@no-rustfix +#![warn(clippy::mutex_atomic, clippy::mutex_integer)] + +use std::sync::Mutex; + +fn issue13378() { + static MTX: Mutex = Mutex::new(0); + //~^ mutex_integer + + // unfixable because we don't fix this `lock` + let mut guard = MTX.lock().unwrap(); + *guard += 1; +} diff --git a/tests/ui/mutex_atomic_unfixable.stderr b/tests/ui/mutex_atomic_unfixable.stderr new file mode 100644 index 0000000000000..27ffb1304c695 --- /dev/null +++ b/tests/ui/mutex_atomic_unfixable.stderr @@ -0,0 +1,17 @@ +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic_unfixable.rs:7:30 + | +LL | static MTX: Mutex = Mutex::new(0); + | ^^^^^^^^^^^^^ + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` + = note: `-D clippy::mutex-integer` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mutex_integer)]` +help: try + | +LL - static MTX: Mutex = Mutex::new(0); +LL + static MTX: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0); + | + +error: aborting due to 1 previous error + From 918b2d88e9fd3a8b23731a0adc913c76b6dfc67f Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 10 Jun 2025 18:17:09 +0000 Subject: [PATCH 82/89] Diagnose liveness on MIR. --- tests/ui/needless_match.fixed | 2 +- tests/ui/needless_match.rs | 2 +- tests/ui/useless_conversion.fixed | 2 +- tests/ui/useless_conversion.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ui/needless_match.fixed b/tests/ui/needless_match.fixed index 41acf44023f63..57273012a192b 100644 --- a/tests/ui/needless_match.fixed +++ b/tests/ui/needless_match.fixed @@ -1,7 +1,7 @@ #![warn(clippy::needless_match)] #![allow(clippy::manual_map)] #![allow(dead_code)] - +#![allow(unused)] #[derive(Clone, Copy)] enum Simple { A, diff --git a/tests/ui/needless_match.rs b/tests/ui/needless_match.rs index 936653b961bbb..3eb577868f2b4 100644 --- a/tests/ui/needless_match.rs +++ b/tests/ui/needless_match.rs @@ -1,7 +1,7 @@ #![warn(clippy::needless_match)] #![allow(clippy::manual_map)] #![allow(dead_code)] - +#![allow(unused)] #[derive(Clone, Copy)] enum Simple { A, diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index ad30c94f34781..2942f64741e91 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -1,5 +1,5 @@ #![deny(clippy::useless_conversion)] -#![allow(clippy::needless_if, clippy::unnecessary_wraps)] +#![allow(clippy::needless_if, clippy::unnecessary_wraps, unused)] // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index 505afb340009a..f2da414e9f652 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -1,5 +1,5 @@ #![deny(clippy::useless_conversion)] -#![allow(clippy::needless_if, clippy::unnecessary_wraps)] +#![allow(clippy::needless_if, clippy::unnecessary_wraps, unused)] // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] From 9be213666e538c083f400bd30fbe1b32c0b07d77 Mon Sep 17 00:00:00 2001 From: Paul MIALANE Date: Tue, 7 Oct 2025 11:06:21 +0200 Subject: [PATCH 83/89] feat(multiple_inherent_impl): Add config option to target specific scope --- CHANGELOG.md | 1 + book/src/lint_configuration.md | 10 ++++ clippy_config/src/conf.rs | 11 ++-- clippy_config/src/types.rs | 8 +++ clippy_lints/src/inherent_impl.rs | 55 ++++++++++++++---- clippy_lints/src/lib.rs | 2 +- .../config_fail/Cargo.stderr | 7 +++ .../config_fail/Cargo.toml | 7 +++ .../config_fail/clippy.toml | 1 + .../config_fail/src/main.rs | 3 + .../crate_fail/Cargo.stderr | 57 ++++++++++++++++++ .../crate_fail/Cargo.toml | 7 +++ .../crate_fail/clippy.toml | 1 + .../crate_fail/src/b.rs | 6 ++ .../crate_fail/src/main.rs | 41 +++++++++++++ .../file_fail/Cargo.stderr | 58 +++++++++++++++++++ .../file_fail/Cargo.toml | 7 +++ .../file_fail/clippy.toml | 1 + .../multiple_inherent_impl/file_fail/src/c.rs | 21 +++++++ .../file_fail/src/main.rs | 40 +++++++++++++ .../module_fail/Cargo.stderr | 41 +++++++++++++ .../module_fail/Cargo.toml | 7 +++ .../module_fail/clippy.toml | 1 + .../module_fail/src/c.rs | 15 +++++ .../module_fail/src/main.rs | 40 +++++++++++++ .../toml_unknown_key/conf_unknown_key.stderr | 3 + tests/ui/impl.rs | 15 ++++- tests/ui/impl.stderr | 16 ++++- 28 files changed, 463 insertions(+), 19 deletions(-) create mode 100644 tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.stderr create mode 100644 tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.toml create mode 100644 tests/ui-cargo/multiple_inherent_impl/config_fail/clippy.toml create mode 100644 tests/ui-cargo/multiple_inherent_impl/config_fail/src/main.rs create mode 100644 tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.stderr create mode 100644 tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.toml create mode 100644 tests/ui-cargo/multiple_inherent_impl/crate_fail/clippy.toml create mode 100644 tests/ui-cargo/multiple_inherent_impl/crate_fail/src/b.rs create mode 100644 tests/ui-cargo/multiple_inherent_impl/crate_fail/src/main.rs create mode 100644 tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.stderr create mode 100644 tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.toml create mode 100644 tests/ui-cargo/multiple_inherent_impl/file_fail/clippy.toml create mode 100644 tests/ui-cargo/multiple_inherent_impl/file_fail/src/c.rs create mode 100644 tests/ui-cargo/multiple_inherent_impl/file_fail/src/main.rs create mode 100644 tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.stderr create mode 100644 tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.toml create mode 100644 tests/ui-cargo/multiple_inherent_impl/module_fail/clippy.toml create mode 100644 tests/ui-cargo/multiple_inherent_impl/module_fail/src/c.rs create mode 100644 tests/ui-cargo/multiple_inherent_impl/module_fail/src/main.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 30781d3d33fba..f000bf071fae9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6874,6 +6874,7 @@ Released 2018-09-13 [`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 +[`inherent-impl-lint-scope`]: https://doc.rust-lang.org/clippy/lint_configuration.html#inherent-impl-lint-scope [`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold [`lint-commented-code`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-commented-code [`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index b2ba19631f133..0e502780647b2 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -671,6 +671,16 @@ A list of paths to types that should be treated as if they do not contain interi * [`mutable_key_type`](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type) +## `inherent-impl-lint-scope` +Sets the scope ("crate", "file", or "module") in which duplicate inherent `impl` blocks for the same type are linted. + +**Default Value:** `"crate"` + +--- +**Affected lints:** +* [`multiple_inherent_impl`](https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl) + + ## `large-error-threshold` The maximum size of the `Err`-variant in a `Result` returned from a function diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 9ad434604dfcf..8e251d372b971 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -1,9 +1,9 @@ use crate::ClippyConfiguration; use crate::types::{ - DisallowedPath, DisallowedPathWithoutReplacement, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, - Rename, SourceItemOrdering, SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, - SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, - SourceItemOrderingWithinModuleItemGroupings, + DisallowedPath, DisallowedPathWithoutReplacement, InherentImplLintScope, MacroMatcher, MatchLintBehaviour, + PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingCategory, + SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, + SourceItemOrderingTraitAssocItemKinds, SourceItemOrderingWithinModuleItemGroupings, }; use clippy_utils::msrvs::Msrv; use itertools::Itertools; @@ -663,6 +663,9 @@ define_Conf! { /// A list of paths to types that should be treated as if they do not contain interior mutability #[lints(borrow_interior_mutable_const, declare_interior_mutable_const, ifs_same_cond, mutable_key_type)] ignore_interior_mutability: Vec = Vec::from(["bytes::Bytes".into()]), + /// Sets the scope ("crate", "file", or "module") in which duplicate inherent `impl` blocks for the same type are linted. + #[lints(multiple_inherent_impl)] + inherent_impl_lint_scope: InherentImplLintScope = InherentImplLintScope::Crate, /// The maximum size of the `Err`-variant in a `Result` returned from a function #[lints(result_large_err)] large_error_threshold: u64 = 128, diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index f64eefa0c232d..7a2bc01cd0e20 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -698,3 +698,11 @@ pub enum PubUnderscoreFieldsBehaviour { PubliclyExported, AllPubFields, } + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum InherentImplLintScope { + Crate, + File, + Module, +} diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index 309d2dfb28b87..a08efbc52d457 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -1,17 +1,23 @@ +use clippy_config::Conf; +use clippy_config::types::InherentImplLintScope; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_lint_allowed; +use clippy_utils::fulfill_or_allowed; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def_id::LocalDefId; +use rustc_hir::def_id::{LocalDefId, LocalModDefId}; use rustc_hir::{Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; -use rustc_span::Span; +use rustc_session::impl_lint_pass; +use rustc_span::{FileName, Span}; use std::collections::hash_map::Entry; declare_clippy_lint! { /// ### What it does /// Checks for multiple inherent implementations of a struct /// + /// The config option controls the scope in which multiple inherent `impl` blocks for the same + /// struct are linted, allowing values of `module` (only within the same module), `file` + /// (within the same file), or `crate` (anywhere in the crate, default). + /// /// ### Why restrict this? /// Splitting the implementation of a type makes the code harder to navigate. /// @@ -41,7 +47,26 @@ declare_clippy_lint! { "Multiple inherent impl that could be grouped" } -declare_lint_pass!(MultipleInherentImpl => [MULTIPLE_INHERENT_IMPL]); +impl_lint_pass!(MultipleInherentImpl => [MULTIPLE_INHERENT_IMPL]); + +pub struct MultipleInherentImpl { + scope: InherentImplLintScope, +} + +impl MultipleInherentImpl { + pub fn new(conf: &'static Conf) -> Self { + Self { + scope: conf.inherent_impl_lint_scope, + } + } +} + +#[derive(Hash, Eq, PartialEq, Clone)] +enum Criterion { + Module(LocalModDefId), + File(FileName), + Crate, +} impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { @@ -55,18 +80,27 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { for (&id, impl_ids) in &impls.inherent_impls { if impl_ids.len() < 2 - // Check for `#[allow]` on the type definition - || is_lint_allowed( + // Check for `#[expect]` or `#[allow]` on the type definition + || fulfill_or_allowed( cx, MULTIPLE_INHERENT_IMPL, - cx.tcx.local_def_id_to_hir_id(id), + [cx.tcx.local_def_id_to_hir_id(id)], ) { continue; } for impl_id in impl_ids.iter().map(|id| id.expect_local()) { let impl_ty = cx.tcx.type_of(impl_id).instantiate_identity(); - match type_map.entry(impl_ty) { + let hir_id = cx.tcx.local_def_id_to_hir_id(impl_id); + let criterion = match self.scope { + InherentImplLintScope::Module => Criterion::Module(cx.tcx.parent_module(hir_id)), + InherentImplLintScope::File => { + let span = cx.tcx.hir_span(hir_id); + Criterion::File(cx.tcx.sess.source_map().lookup_source_file(span.lo()).name.clone()) + }, + InherentImplLintScope::Crate => Criterion::Crate, + }; + match type_map.entry((impl_ty, criterion)) { Entry::Vacant(e) => { // Store the id for the first impl block of this type. The span is retrieved lazily. e.insert(IdOrSpan::Id(impl_id)); @@ -97,7 +131,6 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { // Switching to the next type definition, no need to keep the current entries around. type_map.clear(); } - // `TyCtxt::crate_inherent_impls` doesn't have a defined order. Sort the lint output first. lint_spans.sort_by_key(|x| x.0.lo()); for (span, first_span) in lint_spans { @@ -125,7 +158,7 @@ fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option { { (!span.from_expansion() && impl_item.generics.params.is_empty() - && !is_lint_allowed(cx, MULTIPLE_INHERENT_IMPL, id)) + && !fulfill_or_allowed(cx, MULTIPLE_INHERENT_IMPL, [id])) .then_some(span) } else { None diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6bdc54be82aa9..93e44cacd3224 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -587,7 +587,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings)); store.register_late_pass(|_| Box::new(suspicious_trait_impl::SuspiciousImpl)); store.register_late_pass(|_| Box::new(map_unit_fn::MapUnit)); - store.register_late_pass(|_| Box::new(inherent_impl::MultipleInherentImpl)); + store.register_late_pass(move |_| Box::new(inherent_impl::MultipleInherentImpl::new(conf))); store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)); store.register_late_pass(move |_| Box::new(unwrap::Unwrap::new(conf))); store.register_late_pass(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf))); diff --git a/tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.stderr b/tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.stderr new file mode 100644 index 0000000000000..51c46e4f97b2c --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.stderr @@ -0,0 +1,7 @@ +error: error reading Clippy's configuration file: unknown variant `FooBar`, expected one of `crate`, `file`, `module` + --> $DIR/tests/ui-cargo/multiple_inherent_impl/config_fail/clippy.toml:1:28 + | +1 | inherent-impl-lint-scope = "FooBar" + | ^^^^^^^^ + +error: could not compile `config_fail` (bin "config_fail") due to 1 previous error diff --git a/tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.toml b/tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.toml new file mode 100644 index 0000000000000..0a4cb6e368f67 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/config_fail/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "config_fail" +version = "0.1.0" +edition = "2024" +publish = false + +[dependencies] diff --git a/tests/ui-cargo/multiple_inherent_impl/config_fail/clippy.toml b/tests/ui-cargo/multiple_inherent_impl/config_fail/clippy.toml new file mode 100644 index 0000000000000..7ad426743fb8b --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/config_fail/clippy.toml @@ -0,0 +1 @@ +inherent-impl-lint-scope = "FooBar" diff --git a/tests/ui-cargo/multiple_inherent_impl/config_fail/src/main.rs b/tests/ui-cargo/multiple_inherent_impl/config_fail/src/main.rs new file mode 100644 index 0000000000000..7d5e783022480 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/config_fail/src/main.rs @@ -0,0 +1,3 @@ +#![allow(dead_code)] +#![deny(clippy::multiple_inherent_impl)] +fn main() {} diff --git a/tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.stderr b/tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.stderr new file mode 100644 index 0000000000000..15e0086cf99cc --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.stderr @@ -0,0 +1,57 @@ +error: multiple implementations of this structure + --> src/main.rs:11:1 + | +11 | / impl S { +12 | | //^ Must trigger +13 | | fn second() {} +14 | | } + | |_^ + | +note: first implementation here + --> src/main.rs:7:1 + | + 7 | / impl S { + 8 | | fn first() {} + 9 | | } + | |_^ +note: the lint level is defined here + --> src/main.rs:2:9 + | + 2 | #![deny(clippy::multiple_inherent_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: multiple implementations of this structure + --> src/main.rs:22:5 + | +22 | / impl T { +23 | | //^ Must trigger +24 | | fn second() {} +25 | | } + | |_____^ + | +note: first implementation here + --> src/main.rs:16:1 + | +16 | / impl T { +17 | | fn first() {} +18 | | } + | |_^ + +error: multiple implementations of this structure + --> src/main.rs:36:1 + | +36 | / impl b::T { +37 | | //^ Must trigger +38 | | fn second() {} +39 | | } + | |_^ + | +note: first implementation here + --> src/b.rs:4:1 + | + 4 | / impl T { + 5 | | fn first() {} + 6 | | } + | |_^ + +error: could not compile `crate_fail` (bin "crate_fail") due to 3 previous errors diff --git a/tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.toml b/tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.toml new file mode 100644 index 0000000000000..a280da1bd8964 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/crate_fail/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "crate_fail" +version = "0.1.0" +edition = "2024" +publish = false + +[dependencies] diff --git a/tests/ui-cargo/multiple_inherent_impl/crate_fail/clippy.toml b/tests/ui-cargo/multiple_inherent_impl/crate_fail/clippy.toml new file mode 100644 index 0000000000000..262e42e6fccad --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/crate_fail/clippy.toml @@ -0,0 +1 @@ +inherent-impl-lint-scope = "crate" diff --git a/tests/ui-cargo/multiple_inherent_impl/crate_fail/src/b.rs b/tests/ui-cargo/multiple_inherent_impl/crate_fail/src/b.rs new file mode 100644 index 0000000000000..9d01e61925f75 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/crate_fail/src/b.rs @@ -0,0 +1,6 @@ +pub struct S; +pub struct T; + +impl T { + fn first() {} +} diff --git a/tests/ui-cargo/multiple_inherent_impl/crate_fail/src/main.rs b/tests/ui-cargo/multiple_inherent_impl/crate_fail/src/main.rs new file mode 100644 index 0000000000000..ad95a42955093 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/crate_fail/src/main.rs @@ -0,0 +1,41 @@ +#![allow(dead_code)] +#![deny(clippy::multiple_inherent_impl)] + +struct S; +struct T; + +impl S { + fn first() {} +} + +impl S { + //^ Must trigger + fn second() {} +} + +impl T { + fn first() {} +} + +mod a { + use super::T; + impl T { + //^ Must trigger + fn second() {} + } +} + +mod b; + +impl b::S { + //^ Must NOT trigger + fn first() {} + fn second() {} +} + +impl b::T { + //^ Must trigger + fn second() {} +} + +fn main() {} diff --git a/tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.stderr b/tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.stderr new file mode 100644 index 0000000000000..eb7cf4e0e6ffb --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.stderr @@ -0,0 +1,58 @@ +error: multiple implementations of this structure + --> src/main.rs:13:5 + | +13 | / impl S { +14 | | //^ Must trigger +15 | | fn second() {} +16 | | } + | |_____^ + | +note: first implementation here + --> src/main.rs:6:1 + | + 6 | / impl S { + 7 | | fn first() {} + 8 | | } + | |_^ +note: the lint level is defined here + --> src/main.rs:2:9 + | + 2 | #![deny(clippy::multiple_inherent_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: multiple implementations of this structure + --> src/main.rs:26:5 + | +26 | / impl S { +27 | | //^ Must trigger +28 | | +29 | | fn second() {} +30 | | } + | |_____^ + | +note: first implementation here + --> src/main.rs:22:5 + | +22 | / impl S { +23 | | fn first() {} +24 | | } + | |_____^ + +error: multiple implementations of this structure + --> src/c.rs:17:5 + | +17 | / impl T { +18 | | //^ Must trigger +19 | | fn second() {} +20 | | } + | |_____^ + | +note: first implementation here + --> src/c.rs:10:5 + | +10 | / impl T { +11 | | fn first() {} +12 | | } + | |_____^ + +error: could not compile `file_fail` (bin "file_fail") due to 3 previous errors diff --git a/tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.toml b/tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.toml new file mode 100644 index 0000000000000..7f767c65b98cb --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/file_fail/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "file_fail" +version = "0.1.0" +edition = "2024" +publish = false + +[dependencies] diff --git a/tests/ui-cargo/multiple_inherent_impl/file_fail/clippy.toml b/tests/ui-cargo/multiple_inherent_impl/file_fail/clippy.toml new file mode 100644 index 0000000000000..4229797a91f36 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/file_fail/clippy.toml @@ -0,0 +1 @@ +inherent-impl-lint-scope = "file" diff --git a/tests/ui-cargo/multiple_inherent_impl/file_fail/src/c.rs b/tests/ui-cargo/multiple_inherent_impl/file_fail/src/c.rs new file mode 100644 index 0000000000000..7757061e87b04 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/file_fail/src/c.rs @@ -0,0 +1,21 @@ +pub struct S; +struct T; + +impl S { + fn first() {} +} + +mod d { + use super::T; + impl T { + fn first() {} + } +} + +mod e { + use super::T; + impl T { + //^ Must trigger + fn second() {} + } +} diff --git a/tests/ui-cargo/multiple_inherent_impl/file_fail/src/main.rs b/tests/ui-cargo/multiple_inherent_impl/file_fail/src/main.rs new file mode 100644 index 0000000000000..97514fd30e047 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/file_fail/src/main.rs @@ -0,0 +1,40 @@ +#![allow(dead_code)] +#![deny(clippy::multiple_inherent_impl)] + +struct S; + +impl S { + fn first() {} +} + +mod a { + use super::S; + + impl S { + //^ Must trigger + fn second() {} + } +} + +mod b { + struct S; + + impl S { + fn first() {} + } + + impl S { + //^ Must trigger + + fn second() {} + } +} + +mod c; + +impl c::S { + //^ Must NOT trigger + fn second() {} +} + +fn main() {} diff --git a/tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.stderr b/tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.stderr new file mode 100644 index 0000000000000..dd02afa1d39b3 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.stderr @@ -0,0 +1,41 @@ +error: multiple implementations of this structure + --> src/main.rs:26:5 + | +26 | / impl S { +27 | | //^ Must trigger +28 | | +29 | | fn second() {} +30 | | } + | |_____^ + | +note: first implementation here + --> src/main.rs:22:5 + | +22 | / impl S { +23 | | fn first() {} +24 | | } + | |_____^ +note: the lint level is defined here + --> src/main.rs:2:9 + | + 2 | #![deny(clippy::multiple_inherent_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: multiple implementations of this structure + --> src/c.rs:12:1 + | +12 | / impl T { +13 | | //^ Must trigger +14 | | fn second() {} +15 | | } + | |_^ + | +note: first implementation here + --> src/c.rs:8:1 + | + 8 | / impl T { + 9 | | fn first() {} +10 | | } + | |_^ + +error: could not compile `module_fail` (bin "module_fail") due to 2 previous errors diff --git a/tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.toml b/tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.toml new file mode 100644 index 0000000000000..4b57af0d8fefb --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/module_fail/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "module_fail" +version = "0.1.0" +edition = "2024" +publish = false + +[dependencies] diff --git a/tests/ui-cargo/multiple_inherent_impl/module_fail/clippy.toml b/tests/ui-cargo/multiple_inherent_impl/module_fail/clippy.toml new file mode 100644 index 0000000000000..293dfa183fcea --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/module_fail/clippy.toml @@ -0,0 +1 @@ +inherent-impl-lint-scope = "module" diff --git a/tests/ui-cargo/multiple_inherent_impl/module_fail/src/c.rs b/tests/ui-cargo/multiple_inherent_impl/module_fail/src/c.rs new file mode 100644 index 0000000000000..1d51ebe5d22a0 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/module_fail/src/c.rs @@ -0,0 +1,15 @@ +pub struct S; +struct T; + +impl S { + fn first() {} +} + +impl T { + fn first() {} +} + +impl T { + //^ Must trigger + fn second() {} +} diff --git a/tests/ui-cargo/multiple_inherent_impl/module_fail/src/main.rs b/tests/ui-cargo/multiple_inherent_impl/module_fail/src/main.rs new file mode 100644 index 0000000000000..17b1b777f7be0 --- /dev/null +++ b/tests/ui-cargo/multiple_inherent_impl/module_fail/src/main.rs @@ -0,0 +1,40 @@ +#![allow(dead_code)] +#![deny(clippy::multiple_inherent_impl)] + +struct S; + +impl S { + fn first() {} +} + +mod a { + use super::S; + + impl S { + //^ Must NOT trigger + fn second() {} + } +} + +mod b { + struct S; + + impl S { + fn first() {} + } + + impl S { + //^ Must trigger + + fn second() {} + } +} + +mod c; + +impl c::S { + //^ Must NOT trigger + fn second() {} +} + +fn main() {} diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 20aeb4bb8498e..85401dcd432c7 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -49,6 +49,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect excessive-nesting-threshold future-size-threshold ignore-interior-mutability + inherent-impl-lint-scope large-error-threshold lint-commented-code literal-representation-threshold @@ -144,6 +145,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect excessive-nesting-threshold future-size-threshold ignore-interior-mutability + inherent-impl-lint-scope large-error-threshold lint-commented-code literal-representation-threshold @@ -239,6 +241,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni excessive-nesting-threshold future-size-threshold ignore-interior-mutability + inherent-impl-lint-scope large-error-threshold lint-commented-code literal-representation-threshold diff --git a/tests/ui/impl.rs b/tests/ui/impl.rs index 1b9e4a5cdee13..15cb61c6eebd9 100644 --- a/tests/ui/impl.rs +++ b/tests/ui/impl.rs @@ -68,7 +68,20 @@ struct OneAllowedImpl; impl OneAllowedImpl {} #[allow(clippy::multiple_inherent_impl)] impl OneAllowedImpl {} -impl OneAllowedImpl {} // Lint, only one of the three blocks is allowed. +impl OneAllowedImpl {} +//~^ multiple_inherent_impl + +#[expect(clippy::multiple_inherent_impl)] +struct ExpectedFulfilled; + +impl ExpectedFulfilled {} +impl ExpectedFulfilled {} + +struct OneExpected; +impl OneExpected {} +#[expect(clippy::multiple_inherent_impl)] +impl OneExpected {} +impl OneExpected {} //~^ multiple_inherent_impl fn main() {} diff --git a/tests/ui/impl.stderr b/tests/ui/impl.stderr index 355927b782537..93d4b3998f908 100644 --- a/tests/ui/impl.stderr +++ b/tests/ui/impl.stderr @@ -57,7 +57,7 @@ LL | | } error: multiple implementations of this structure --> tests/ui/impl.rs:71:1 | -LL | impl OneAllowedImpl {} // Lint, only one of the three blocks is allowed. +LL | impl OneAllowedImpl {} | ^^^^^^^^^^^^^^^^^^^^^^ | note: first implementation here @@ -66,5 +66,17 @@ note: first implementation here LL | impl OneAllowedImpl {} | ^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: multiple implementations of this structure + --> tests/ui/impl.rs:84:1 + | +LL | impl OneExpected {} + | ^^^^^^^^^^^^^^^^^^^ + | +note: first implementation here + --> tests/ui/impl.rs:81:1 + | +LL | impl OneExpected {} + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors From e786e009ed32af91db404426a64e9c3daaf70587 Mon Sep 17 00:00:00 2001 From: alexey semenyuk Date: Sun, 12 Oct 2025 21:06:05 +0300 Subject: [PATCH 84/89] actions/setup-node update --- .github/workflows/remark.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index c9d350ee0b304..c2cc48ab95116 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -17,9 +17,9 @@ jobs: persist-credentials: false - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v5 with: - node-version: '18.x' + node-version: '20.x' - name: Install remark run: npm install remark-cli remark-lint remark-lint-maximum-line-length@^3.1.3 remark-preset-lint-recommended remark-gfm From 1bd8cad5a2b27ffb3f6e3c28c9e05ba766c19878 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Fri, 10 Oct 2025 19:18:38 -0400 Subject: [PATCH 85/89] Allow `explicit_write` in tests --- clippy_lints/src/explicit_write.rs | 7 ++++++- tests/ui/explicit_write_in_test.rs | 9 +++++++++ tests/ui/explicit_write_in_test.stderr | 0 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 tests/ui/explicit_write_in_test.rs create mode 100644 tests/ui/explicit_write_in_test.stderr diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 2a3b75ecae1b9..c59ffa14a5fee 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span}; use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_expn_of, sym}; +use clippy_utils::{is_expn_of, is_in_test, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{BindingMode, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind}; @@ -72,6 +72,11 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { return; }; + // Performing an explicit write in a test circumvent's libtest's capture of stdio and stdout. + if is_in_test(cx.tcx, expr.hir_id) { + return; + } + // ordering is important here, since `writeln!` uses `write!` internally let calling_macro = if is_expn_of(write_call.span, sym::writeln).is_some() { Some("writeln") diff --git a/tests/ui/explicit_write_in_test.rs b/tests/ui/explicit_write_in_test.rs new file mode 100644 index 0000000000000..df020b7f1382a --- /dev/null +++ b/tests/ui/explicit_write_in_test.rs @@ -0,0 +1,9 @@ +//@ check-pass +#![warn(clippy::explicit_write)] + +#[test] +fn test() { + use std::io::Write; + writeln!(std::io::stderr(), "I am an explicit write.").unwrap(); + eprintln!("I am not an explicit write."); +} diff --git a/tests/ui/explicit_write_in_test.stderr b/tests/ui/explicit_write_in_test.stderr new file mode 100644 index 0000000000000..e69de29bb2d1d From 9cc02915824821a76ba0e405506df6bb08719c35 Mon Sep 17 00:00:00 2001 From: Teodoro Freund Date: Mon, 15 Sep 2025 14:06:48 +0100 Subject: [PATCH 86/89] `unnecessary_safety_comment` fix an ICE and improve coverage Considering comments above attributes for items Fixed the ICE and safety comments between attributes - No longer using attr.span() - ignoring attributes manually Improve error messages on unsafe fns --- .../src/undocumented_unsafe_blocks.rs | 227 ++++++++++-------- .../fail/Cargo.stderr | 8 +- .../undocumented_unsafe_blocks.default.stderr | 136 ++++++++++- ...undocumented_unsafe_blocks.disabled.stderr | 98 +++++++- .../undocumented_unsafe_blocks.rs | 62 +++++ ...mented_unsafe_blocks_fixable.default.fixed | 20 ++ ...ented_unsafe_blocks_fixable.default.stderr | 22 ++ ...ented_unsafe_blocks_fixable.disabled.fixed | 20 ++ ...nted_unsafe_blocks_fixable.disabled.stderr | 22 ++ .../undocumented_unsafe_blocks_fixable.rs | 20 ++ tests/ui/crashes/ice-15684.rs | 10 + tests/ui/crashes/ice-15684.stderr | 16 ++ tests/ui/unnecessary_safety_comment.stderr | 36 +-- 13 files changed, 564 insertions(+), 133 deletions(-) create mode 100644 tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.fixed create mode 100644 tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.stderr create mode 100644 tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.fixed create mode 100644 tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.stderr create mode 100644 tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs create mode 100644 tests/ui/crashes/ice-15684.rs create mode 100644 tests/ui/crashes/ice-15684.stderr diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index b37f2a27f9051..9afa9d65c2613 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -7,8 +7,8 @@ use clippy_utils::is_lint_allowed; use clippy_utils::source::walk_span_to_context; use clippy_utils::visitors::{Descend, for_each_expr}; use hir::HirId; -use rustc_hir as hir; -use rustc_hir::{Block, BlockCheckMode, Impl, ItemKind, Node, UnsafeSource}; +use rustc_errors::Applicability; +use rustc_hir::{self as hir, Block, BlockCheckMode, FnSig, Impl, ItemKind, Node, UnsafeSource}; use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; @@ -113,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { && !block.span.in_external_macro(cx.tcx.sess.source_map()) && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id) && !is_unsafe_from_proc_macro(cx, block.span) - && !block_has_safety_comment(cx, block.span) + && !block_has_safety_comment(cx, block.span, self.accept_comment_above_attributes) && !block_parents_have_safety_comment( self.accept_comment_above_statement, self.accept_comment_above_attributes, @@ -143,7 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { if let Some(tail) = block.expr && !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, tail.hir_id) && !tail.span.in_external_macro(cx.tcx.sess.source_map()) - && let HasSafetyComment::Yes(pos) = + && let HasSafetyComment::Yes(pos, _) = stmt_has_safety_comment(cx, tail.span, tail.hir_id, self.accept_comment_above_attributes) && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, tail, pos) { @@ -168,7 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { }; if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, stmt.hir_id) && !stmt.span.in_external_macro(cx.tcx.sess.source_map()) - && let HasSafetyComment::Yes(pos) = + && let HasSafetyComment::Yes(pos, _) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id, self.accept_comment_above_attributes) && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, expr, pos) { @@ -191,8 +191,12 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { let mk_spans = |pos: BytePos| { let source_map = cx.tcx.sess.source_map(); - let span = Span::new(pos, pos, SyntaxContext::root(), None); - let help_span = source_map.span_extend_to_next_char(span, '\n', true); + let help_span = Span::new( + pos, + pos + BytePos(u32::try_from("SAFETY:".len()).unwrap()), + SyntaxContext::root(), + None, + ); let span = if source_map.is_multiline(item.span) { source_map.span_until_char(item.span, '\n') } else { @@ -201,16 +205,16 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { (span, help_span) }; - let item_has_safety_comment = item_has_safety_comment(cx, item); + let item_has_safety_comment = item_has_safety_comment(cx, item, self.accept_comment_above_attributes); match item_has_safety_comment { - HasSafetyComment::Yes(pos) => check_has_safety_comment(cx, item, mk_spans(pos)), + HasSafetyComment::Yes(pos, is_doc) => check_has_safety_comment(cx, item, mk_spans(pos), is_doc), HasSafetyComment::No => check_has_no_safety_comment(cx, item), HasSafetyComment::Maybe => {}, } } } -fn check_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>, (span, help_span): (Span, Span)) { +fn check_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>, (span, help_span): (Span, Span), is_doc: bool) { match &item.kind { ItemKind::Impl(Impl { of_trait: Some(of_trait), @@ -252,6 +256,40 @@ fn check_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>, (span, h } } }, + // Unsafe functions with a SAFETY comment are suggested to change it to a `# Safety` comment + ItemKind::Fn { + sig: FnSig { header, .. }, + .. + } if header.is_unsafe() => { + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { + span_lint_and_then( + cx, + UNNECESSARY_SAFETY_COMMENT, + span, + format!( + "{} has unnecessary safety comment", + cx.tcx.def_descr(item.owner_id.to_def_id()), + ), + |diag| { + if is_doc { + // If it's already within a doc comment, we try to suggest the change + + diag.span_suggestion( + help_span, + "consider changing it to a `# Safety` section", + "# Safety", + Applicability::MachineApplicable, + ); + } else { + diag.span_help( + help_span, + "consider changing the `safety` comment for a `# Safety` doc comment", + ); + } + }, + ); + } + }, // Aside from unsafe impls and consts/statics with an unsafe block, items in general // do not have safety invariants that need to be documented, so lint those. _ => { @@ -272,6 +310,7 @@ fn check_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>, (span, h }, } } + fn check_has_no_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) { if let ItemKind::Impl(Impl { of_trait: Some(of_trait), @@ -407,21 +446,21 @@ fn block_parents_have_safety_comment( cx: &LateContext<'_>, id: HirId, ) -> bool { - let (span, hir_id) = match cx.tcx.parent_hir_node(id) { - Node::Expr(expr) if let Some(inner) = find_unsafe_block_parent_in_expr(cx, expr) => inner, + let span = match cx.tcx.parent_hir_node(id) { + Node::Expr(expr) if let Some((span, _)) = find_unsafe_block_parent_in_expr(cx, expr) => span, Node::Stmt(hir::Stmt { kind: - hir::StmtKind::Let(hir::LetStmt { span, hir_id, .. }) - | hir::StmtKind::Expr(hir::Expr { span, hir_id, .. }) - | hir::StmtKind::Semi(hir::Expr { span, hir_id, .. }), + hir::StmtKind::Let(hir::LetStmt { span, .. }) + | hir::StmtKind::Expr(hir::Expr { span, .. }) + | hir::StmtKind::Semi(hir::Expr { span, .. }), .. }) - | Node::LetStmt(hir::LetStmt { span, hir_id, .. }) => (*span, *hir_id), + | Node::LetStmt(hir::LetStmt { span, .. }) => *span, - node if let Some((span, hir_id)) = span_and_hid_of_item_alike_node(&node) + node if let Some((span, _)) = span_and_hid_of_item_alike_node(&node) && is_const_or_static(&node) => { - (span, hir_id) + span }, _ => return false, @@ -429,24 +468,7 @@ fn block_parents_have_safety_comment( // if unsafe block is part of a let/const/static statement, // and accept_comment_above_statement is set to true // we accept the safety comment in the line the precedes this statement. - accept_comment_above_statement - && span_with_attrs_has_safety_comment(cx, span, hir_id, accept_comment_above_attributes) -} - -/// Extends `span` to also include its attributes, then checks if that span has a safety comment. -fn span_with_attrs_has_safety_comment( - cx: &LateContext<'_>, - span: Span, - hir_id: HirId, - accept_comment_above_attributes: bool, -) -> bool { - let span = if accept_comment_above_attributes { - include_attrs_in_span(cx, hir_id, span) - } else { - span - }; - - span_has_safety_comment(cx, span) + accept_comment_above_statement && span_has_safety_comment(cx, span, accept_comment_above_attributes) } /// Checks if an expression is "branchy", e.g. loop, match/if/etc. @@ -458,7 +480,7 @@ fn is_branchy(expr: &hir::Expr<'_>) -> bool { } /// Checks if the lines immediately preceding the block contain a safety comment. -fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { +fn block_has_safety_comment(cx: &LateContext<'_>, span: Span, accept_comment_above_attributes: bool) -> bool { // This intentionally ignores text before the start of a function so something like: // ``` // // SAFETY: reason @@ -468,29 +490,25 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { // attributes and doc comments. matches!( - span_from_macro_expansion_has_safety_comment(cx, span), - HasSafetyComment::Yes(_) - ) || span_has_safety_comment(cx, span) -} - -fn include_attrs_in_span(cx: &LateContext<'_>, hir_id: HirId, span: Span) -> Span { - span.to(cx.tcx.hir_attrs(hir_id).iter().fold(span, |acc, attr| { - if attr.is_doc_comment() { - return acc; - } - acc.to(attr.span()) - })) + span_from_macro_expansion_has_safety_comment(cx, span, accept_comment_above_attributes), + HasSafetyComment::Yes(_, _) + ) || span_has_safety_comment(cx, span, accept_comment_above_attributes) } +#[derive(Debug)] enum HasSafetyComment { - Yes(BytePos), + Yes(BytePos, bool), No, Maybe, } /// Checks if the lines immediately preceding the item contain a safety comment. -fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSafetyComment { - match span_from_macro_expansion_has_safety_comment(cx, item.span) { +fn item_has_safety_comment( + cx: &LateContext<'_>, + item: &hir::Item<'_>, + accept_comment_above_attributes: bool, +) -> HasSafetyComment { + match span_from_macro_expansion_has_safety_comment(cx, item.span, accept_comment_above_attributes) { HasSafetyComment::Maybe => (), has_safety_comment => return has_safety_comment, } @@ -535,15 +553,13 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf return if comment_start_line.line >= unsafe_line.line { HasSafetyComment::No } else { - match text_has_safety_comment( + text_has_safety_comment( src, &unsafe_line.sf.lines() [(comment_start_line.line + usize::from(!include_first_line_of_file))..=unsafe_line.line], unsafe_line.sf.start_pos, - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } + accept_comment_above_attributes, + ) }; } HasSafetyComment::Maybe @@ -556,7 +572,7 @@ fn stmt_has_safety_comment( hir_id: HirId, accept_comment_above_attributes: bool, ) -> HasSafetyComment { - match span_from_macro_expansion_has_safety_comment(cx, span) { + match span_from_macro_expansion_has_safety_comment(cx, span, accept_comment_above_attributes) { HasSafetyComment::Maybe => (), has_safety_comment => return has_safety_comment, } @@ -570,13 +586,6 @@ fn stmt_has_safety_comment( _ => return HasSafetyComment::Maybe, }; - // if span_with_attrs_has_safety_comment(cx, span, hir_id, accept_comment_above_attrib - // } - let mut span = span; - if accept_comment_above_attributes { - span = include_attrs_in_span(cx, hir_id, span); - } - let source_map = cx.sess().source_map(); if let Some(comment_start) = comment_start && let Ok(unsafe_line) = source_map.lookup_line(span.lo()) @@ -587,14 +596,12 @@ fn stmt_has_safety_comment( return if comment_start_line.line >= unsafe_line.line { HasSafetyComment::No } else { - match text_has_safety_comment( + text_has_safety_comment( src, &unsafe_line.sf.lines()[comment_start_line.line + 1..=unsafe_line.line], unsafe_line.sf.start_pos, - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } + accept_comment_above_attributes, + ) }; } HasSafetyComment::Maybe @@ -647,7 +654,11 @@ fn comment_start_before_item_in_mod( }) } -fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> HasSafetyComment { +fn span_from_macro_expansion_has_safety_comment( + cx: &LateContext<'_>, + span: Span, + accept_comment_above_attributes: bool, +) -> HasSafetyComment { let source_map = cx.sess().source_map(); let ctxt = span.ctxt(); if ctxt == SyntaxContext::root() { @@ -663,14 +674,12 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span && let Some(src) = unsafe_line.sf.src.as_deref() { if macro_line.line < unsafe_line.line { - match text_has_safety_comment( + text_has_safety_comment( src, &unsafe_line.sf.lines()[macro_line.line + 1..=unsafe_line.line], unsafe_line.sf.start_pos, - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } + accept_comment_above_attributes, + ) } else { HasSafetyComment::No } @@ -713,7 +722,7 @@ fn get_body_search_span(cx: &LateContext<'_>) -> Option { None } -fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { +fn span_has_safety_comment(cx: &LateContext<'_>, span: Span, accept_comment_above_attributes: bool) -> bool { let source_map = cx.sess().source_map(); let ctxt = span.ctxt(); if ctxt.is_root() @@ -729,12 +738,15 @@ fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { // fn foo() { some_stuff; unsafe { stuff }; other_stuff; } // ^-------------^ body_line.line < unsafe_line.line - && text_has_safety_comment( - src, - &unsafe_line.sf.lines()[body_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos, + && matches!( + text_has_safety_comment( + src, + &unsafe_line.sf.lines()[body_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos, + accept_comment_above_attributes, + ), + HasSafetyComment::Yes(..) ) - .is_some() } else { // Problem getting source text. Pretend a comment was found. true @@ -745,7 +757,15 @@ fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { } /// Checks if the given text has a safety comment for the immediately proceeding line. -fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos: BytePos) -> Option { +/// +/// If `accept_comment_above_attributes` is true, it will ignore attributes inbetween blocks of +/// comments +fn text_has_safety_comment( + src: &str, + line_starts: &[RelativeBytePos], + start_pos: BytePos, + accept_comment_above_attributes: bool, +) -> HasSafetyComment { let mut lines = line_starts .array_windows::<2>() .rev() @@ -756,9 +776,12 @@ fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos let trimmed = text.trim_start(); Some((start + (text.len() - trimmed.len()), trimmed)) }) - .filter(|(_, text)| !text.is_empty()); + .filter(|(_, text)| !(text.is_empty() || (accept_comment_above_attributes && is_attribute(text)))); + + let Some((line_start, line)) = lines.next() else { + return HasSafetyComment::No; + }; - let (line_start, line) = lines.next()?; let mut in_codeblock = false; // Check for a sequence of line comments. if line.starts_with("//") { @@ -771,12 +794,17 @@ fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos in_codeblock = !in_codeblock; } - if line.to_ascii_uppercase().contains("SAFETY:") && !in_codeblock { - return Some(start_pos + BytePos(u32::try_from(line_start).unwrap())); + if !in_codeblock && let Some(safety_pos) = line.to_ascii_uppercase().find("SAFETY:") { + return HasSafetyComment::Yes( + start_pos + + BytePos(u32::try_from(line_start).unwrap()) + + BytePos(u32::try_from(safety_pos).unwrap()), + line.starts_with("///"), + ); } match lines.next() { Some((s, x)) if x.starts_with("//") => (line, line_start) = (x, s), - _ => return None, + _ => return HasSafetyComment::No, } } } @@ -787,19 +815,30 @@ fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos if line.starts_with("/*") { let src = &src[line_start..line_starts.last().unwrap().to_usize()]; let mut tokens = tokenize(src, FrontmatterAllowed::No); - return (src[..tokens.next().unwrap().len as usize] - .to_ascii_uppercase() - .contains("SAFETY:") - && tokens.all(|t| t.kind == TokenKind::Whitespace)) - .then_some(start_pos + BytePos(u32::try_from(line_start).unwrap())); + let a = tokens.next(); + if let Some(safety_pos) = src[..a.unwrap().len as usize].to_ascii_uppercase().find("SAFETY:") + && tokens.all(|t| t.kind == TokenKind::Whitespace) + { + return HasSafetyComment::Yes( + start_pos + + BytePos(u32::try_from(line_start).unwrap()) + + BytePos(u32::try_from(safety_pos).unwrap()), + line.starts_with("/**"), + ); + } + return HasSafetyComment::No; } match lines.next() { Some(x) => (line_start, line) = x, - None => return None, + None => return HasSafetyComment::No, } } } +fn is_attribute(text: &str) -> bool { + (text.starts_with("#[") || text.starts_with("#![")) && text.trim_end().ends_with(']') +} + fn span_and_hid_of_item_alike_node(node: &Node<'_>) -> Option<(Span, HirId)> { match node { Node::Item(item) => Some((item.span, item.owner_id.into())), diff --git a/tests/ui-cargo/undocumented_unsafe_blocks/fail/Cargo.stderr b/tests/ui-cargo/undocumented_unsafe_blocks/fail/Cargo.stderr index 59a7146ac90fd..bfe2486c85029 100644 --- a/tests/ui-cargo/undocumented_unsafe_blocks/fail/Cargo.stderr +++ b/tests/ui-cargo/undocumented_unsafe_blocks/fail/Cargo.stderr @@ -5,10 +5,10 @@ error: module has unnecessary safety comment | ^^^^^^^^ | help: consider removing the safety comment - --> src/main.rs:1:1 + --> src/main.rs:1:4 | 1 | // SAFETY: ... - | ^^^^^^^^^^^^^^ + | ^^^^^^^ = note: requested on the command line with `-D clippy::unnecessary-safety-comment` error: module has unnecessary safety comment @@ -18,9 +18,9 @@ error: module has unnecessary safety comment | ^^^^^^^^ | help: consider removing the safety comment - --> src/main.rs:4:1 + --> src/main.rs:4:4 | 4 | // SAFETY: ... - | ^^^^^^^^^^^^^^ + | ^^^^^^^ error: could not compile `undocumented_unsafe_blocks` (bin "undocumented_unsafe_blocks") due to 2 previous errors 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 bfc14be5421fc..61e5af81d8276 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 @@ -247,10 +247,10 @@ LL | const BIG_NUMBER: i32 = 1000000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:507:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:507:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` @@ -289,10 +289,10 @@ LL | | }; | |______^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:542:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:542:8 | 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:545:12 @@ -342,17 +342,137 @@ LL | const NO_SAFETY_IN_IMPL: i32 = unsafe { 1 }; | = help: consider adding a safety comment on the preceding line +error: constant has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:701:5 + | +LL | const UNIX_EPOCH_JULIAN_DAY: i32 = + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:699:8 + | +LL | // SAFETY: fail ONLY if `accept-comment-above-attribute = false` + | ^^^^^^^ + error: statement has unnecessary safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:719:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:721:5 | LL | _ = bar(); | ^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:718:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:720:8 | LL | // SAFETY: unnecessary_safety_comment triggers here - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: module has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:741:5 + | +LL | mod x {} + | ^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:740:8 + | +LL | // SAFETY: ... + | ^^^^^^^ + +error: module has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:746:5 + | +LL | mod y {} + | ^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:744:8 + | +LL | // SAFETY: ... + | ^^^^^^^ + +error: module has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:751:5 + | +LL | mod z {} + | ^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:750:8 + | +LL | // SAFETY: ... + | ^^^^^^^ + +error: module has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:759:5 + | +LL | mod y {} + | ^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:757:8 + | +LL | // SAFETY: ... + | ^^^^^^^ + +error: statement has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:774:9 + | +LL | let x = 34; + | ^^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:772:12 + | +LL | // SAFETY: ... + | ^^^^^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:781:5 + | +LL | unsafe fn unsafe_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the `safety` comment for a `# Safety` doc comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:780:8 + | +LL | // SAFETY: Bla + | ^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:787:5 + | +LL | unsafe fn unsafe_block_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the `safety` comment for a `# Safety` doc comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:785:8 + | +LL | SAFETY: Bla + | ^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:791:5 + | +LL | fn safe_comment() {} + | ^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:790:8 + | +LL | // SAFETY: Bla + | ^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:795:5 + | +LL | fn safe_doc_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:794:9 + | +LL | /// SAFETY: Bla + | ^^^^^^^ -error: aborting due to 40 previous errors +error: aborting due to 50 previous errors diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr index cebfc48a884f6..e252cffea9160 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 @@ -247,10 +247,10 @@ LL | const BIG_NUMBER: i32 = 1000000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:507:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:507:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` @@ -297,10 +297,10 @@ LL | | }; | |______^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:542:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:542:8 | 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:545:12 @@ -439,24 +439,104 @@ LL | unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }.into_julian = help: consider adding a safety comment on the preceding line error: statement has unnecessary safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:719:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:721:5 | LL | _ = bar(); | ^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:718:5 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:720:8 | LL | // SAFETY: unnecessary_safety_comment triggers here - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe block missing a safety comment - --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:733:12 + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:735:12 | LL | return unsafe { h() }; | ^^^^^^^^^^^^^^ | = help: consider adding a safety comment on the preceding line -error: aborting due to 53 previous errors +error: module has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:741:5 + | +LL | mod x {} + | ^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:740:8 + | +LL | // SAFETY: ... + | ^^^^^^^ + +error: module has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:751:5 + | +LL | mod z {} + | ^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:750:8 + | +LL | // SAFETY: ... + | ^^^^^^^ + +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:766:9 + | +LL | unsafe {} + | ^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:781:5 + | +LL | unsafe fn unsafe_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the `safety` comment for a `# Safety` doc comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:780:8 + | +LL | // SAFETY: Bla + | ^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:787:5 + | +LL | unsafe fn unsafe_block_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider changing the `safety` comment for a `# Safety` doc comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:785:8 + | +LL | SAFETY: Bla + | ^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:791:5 + | +LL | fn safe_comment() {} + | ^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:790:8 + | +LL | // SAFETY: Bla + | ^^^^^^^ + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:795:5 + | +LL | fn safe_doc_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:794:9 + | +LL | /// SAFETY: Bla + | ^^^^^^^ + +error: aborting due to 60 previous errors diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs index a2d7c1b6c7967..db9e81cf10a1a 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs @@ -701,6 +701,8 @@ mod issue_11709_regression { const UNIX_EPOCH_JULIAN_DAY: i32 = unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }.into_julian_day_just_make_this_line_longer(); //~[disabled]^ undocumented_unsafe_blocks + // This shouldn't be linted, Issue #15755 + //~[default]^^^^ unnecessary_safety_comment } fn issue_13039() { @@ -734,4 +736,64 @@ fn rfl_issue15034() -> i32 { //~[disabled]^ ERROR: unsafe block missing a safety comment } +mod issue_14555 { + // SAFETY: ... + mod x {} + //~^ unnecessary_safety_comment + + // SAFETY: ... + #[doc(hidden)] + mod y {} + //~[default]^ unnecessary_safety_comment + + #[doc(hidden)] + // SAFETY: ... + mod z {} + //~^ unnecessary_safety_comment +} + +mod issue_15754 { + #[must_use] + // SAFETY: ... + #[doc(hidden)] + mod y {} + //~[default]^ unnecessary_safety_comment + + fn foo() { + #[doc(hidden)] + // SAFETY: unnecessary_safety_comment should not trigger here + #[allow(unsafe_code)] + unsafe {} + //~[disabled]^ undocumented_unsafe_blocks + } + + fn bar() { + #[doc(hidden)] + // SAFETY: ... + #[allow(clippy::unnecessary_cast)] + let x = 34; + //~[default]^ unnecessary_safety_comment + } +} + +mod unsafe_fns { + // SAFETY: Bla + unsafe fn unsafe_comment() {} + //~^ unnecessary_safety_comment + + /* + SAFETY: Bla + */ + unsafe fn unsafe_block_comment() {} + //~^ unnecessary_safety_comment + + // SAFETY: Bla + fn safe_comment() {} + //~^ unnecessary_safety_comment + + /// SAFETY: Bla + fn safe_doc_comment() {} + //~^ unnecessary_safety_comment +} + fn main() {} diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.fixed b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.fixed new file mode 100644 index 0000000000000..cc8d5028b7276 --- /dev/null +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.fixed @@ -0,0 +1,20 @@ +//@aux-build:../../ui/auxiliary/proc_macro_unsafe.rs +//@revisions: default disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/default +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/disabled + +#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] + +mod unsafe_fns { + /// # Safety Bla + unsafe fn unsafe_doc_comment() {} + //~^ unnecessary_safety_comment + + /** + * # Safety Bla + */ + unsafe fn unsafe_block_doc_comment() {} + //~^ unnecessary_safety_comment +} + +fn main() {} diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.stderr new file mode 100644 index 0000000000000..95e47dc7ed639 --- /dev/null +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.default.stderr @@ -0,0 +1,22 @@ +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs:10:5 + | +LL | /// SAFETY: Bla + | ------- help: consider changing it to a `# Safety` section: `# Safety` +LL | unsafe fn unsafe_doc_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs:16:5 + | +LL | * SAFETY: Bla + | ------- help: consider changing it to a `# Safety` section: `# Safety` +LL | */ +LL | unsafe fn unsafe_block_doc_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.fixed b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.fixed new file mode 100644 index 0000000000000..cc8d5028b7276 --- /dev/null +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.fixed @@ -0,0 +1,20 @@ +//@aux-build:../../ui/auxiliary/proc_macro_unsafe.rs +//@revisions: default disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/default +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/disabled + +#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] + +mod unsafe_fns { + /// # Safety Bla + unsafe fn unsafe_doc_comment() {} + //~^ unnecessary_safety_comment + + /** + * # Safety Bla + */ + unsafe fn unsafe_block_doc_comment() {} + //~^ unnecessary_safety_comment +} + +fn main() {} diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.stderr new file mode 100644 index 0000000000000..95e47dc7ed639 --- /dev/null +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.disabled.stderr @@ -0,0 +1,22 @@ +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs:10:5 + | +LL | /// SAFETY: Bla + | ------- help: consider changing it to a `# Safety` section: `# Safety` +LL | unsafe fn unsafe_doc_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` + +error: function has unnecessary safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs:16:5 + | +LL | * SAFETY: Bla + | ------- help: consider changing it to a `# Safety` section: `# Safety` +LL | */ +LL | unsafe fn unsafe_block_doc_comment() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs new file mode 100644 index 0000000000000..14b91126caa66 --- /dev/null +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks_fixable.rs @@ -0,0 +1,20 @@ +//@aux-build:../../ui/auxiliary/proc_macro_unsafe.rs +//@revisions: default disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/default +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/disabled + +#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] + +mod unsafe_fns { + /// SAFETY: Bla + unsafe fn unsafe_doc_comment() {} + //~^ unnecessary_safety_comment + + /** + * SAFETY: Bla + */ + unsafe fn unsafe_block_doc_comment() {} + //~^ unnecessary_safety_comment +} + +fn main() {} diff --git a/tests/ui/crashes/ice-15684.rs b/tests/ui/crashes/ice-15684.rs new file mode 100644 index 0000000000000..12f36042a0fea --- /dev/null +++ b/tests/ui/crashes/ice-15684.rs @@ -0,0 +1,10 @@ +#![warn(clippy::unnecessary_safety_comment)] + +fn foo() -> i32 { + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` + #[must_use] + return 33; + //~^ unnecessary_safety_comment +} + +fn main() {} diff --git a/tests/ui/crashes/ice-15684.stderr b/tests/ui/crashes/ice-15684.stderr new file mode 100644 index 0000000000000..0d4eb624a2b12 --- /dev/null +++ b/tests/ui/crashes/ice-15684.stderr @@ -0,0 +1,16 @@ +error: statement has unnecessary safety comment + --> tests/ui/crashes/ice-15684.rs:6:5 + | +LL | return 33; + | ^^^^^^^^^^ + | +help: consider removing the safety comment + --> tests/ui/crashes/ice-15684.rs:4:8 + | +LL | // SAFETY: fail ONLY if `accept-comment-above-attribute = false` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/unnecessary_safety_comment.stderr b/tests/ui/unnecessary_safety_comment.stderr index 732e6767c1780..6ad94f986434d 100644 --- a/tests/ui/unnecessary_safety_comment.stderr +++ b/tests/ui/unnecessary_safety_comment.stderr @@ -5,10 +5,10 @@ LL | const CONST: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:5:5 + --> tests/ui/unnecessary_safety_comment.rs:5:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` @@ -19,10 +19,10 @@ LL | static STATIC: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:9:5 + --> tests/ui/unnecessary_safety_comment.rs:9:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ error: struct has unnecessary safety comment --> tests/ui/unnecessary_safety_comment.rs:14:5 @@ -31,10 +31,10 @@ LL | struct Struct; | ^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:13:5 + --> tests/ui/unnecessary_safety_comment.rs:13:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ error: enum has unnecessary safety comment --> tests/ui/unnecessary_safety_comment.rs:18:5 @@ -43,10 +43,10 @@ LL | enum Enum {} | ^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:17:5 + --> tests/ui/unnecessary_safety_comment.rs:17:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ error: module has unnecessary safety comment --> tests/ui/unnecessary_safety_comment.rs:22:5 @@ -55,10 +55,10 @@ LL | mod module {} | ^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:21:5 + --> tests/ui/unnecessary_safety_comment.rs:21:8 | LL | // SAFETY: - | ^^^^^^^^^^ + | ^^^^^^^ error: impl has unnecessary safety comment --> tests/ui/unnecessary_safety_comment.rs:42:13 @@ -70,10 +70,10 @@ LL | with_safety_comment!(i32); | ------------------------- in this macro invocation | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:41:13 + --> tests/ui/unnecessary_safety_comment.rs:41:16 | LL | // Safety: unnecessary - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ = note: this error originates in the macro `with_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: expression has unnecessary safety comment @@ -83,10 +83,10 @@ LL | 24 | ^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:59:5 + --> tests/ui/unnecessary_safety_comment.rs:59:8 | LL | // SAFETY: unnecessary - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ error: statement has unnecessary safety comment --> tests/ui/unnecessary_safety_comment.rs:52:5 @@ -95,10 +95,10 @@ LL | let num = 42; | ^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:51:5 + --> tests/ui/unnecessary_safety_comment.rs:51:8 | LL | // SAFETY: unnecessary - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ error: statement has unnecessary safety comment --> tests/ui/unnecessary_safety_comment.rs:56:5 @@ -107,10 +107,10 @@ LL | if num > 24 {} | ^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> tests/ui/unnecessary_safety_comment.rs:55:5 + --> tests/ui/unnecessary_safety_comment.rs:55:8 | LL | // SAFETY: unnecessary - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to 9 previous errors From e8fec08b9cd9cb4a97eb81ed33f7bbff14f8e9b0 Mon Sep 17 00:00:00 2001 From: Diggory Blake Date: Sun, 6 Jul 2025 20:58:14 +0100 Subject: [PATCH 87/89] Restrict sysroot crate imports to those defined in this repo. It's common to import dependencies from the sysroot via `extern crate` rather than use an explicit cargo dependency, when it's necessary to use the same dependency version as used by rustc itself. However, this is dangerous for crates.io crates, since rustc may not pull in the dependency on some targets, or may pull in multiple versions. In both cases, the `extern crate` fails to resolve. To address this, re-export all such dependencies from the appropriate `rustc_*` crates, and use this alias from crates which would otherwise need to use `extern crate`. --- clippy_lints/src/doc/broken_link.rs | 2 +- clippy_lints/src/doc/mod.rs | 18 ++++++++++-------- clippy_lints/src/lib.rs | 5 ----- clippy_lints/src/methods/ip_constant.rs | 2 +- clippy_lints/src/unnested_or_patterns.rs | 2 +- clippy_utils/src/lib.rs | 3 +-- clippy_utils/src/msrvs.rs | 18 +++++++++--------- 7 files changed, 23 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/doc/broken_link.rs b/clippy_lints/src/doc/broken_link.rs index 8878fa9180fec..2fa41d83915a4 100644 --- a/clippy_lints/src/doc/broken_link.rs +++ b/clippy_lints/src/doc/broken_link.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; -use pulldown_cmark::BrokenLink as PullDownBrokenLink; use rustc_lint::LateContext; +use rustc_resolve::rustdoc::pulldown_cmark::BrokenLink as PullDownBrokenLink; use rustc_resolve::rustdoc::{DocFragment, source_span_for_markdown_range}; use rustc_span::{BytePos, Pos, Span}; diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index f8ae770b3a4db..cfdf9ca623688 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -4,19 +4,21 @@ use clippy_config::Conf; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then}; use clippy_utils::{is_entrypoint_fn, is_trait_impl_item}; -use pulldown_cmark::Event::{ - Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start, - TaskListMarker, Text, -}; -use pulldown_cmark::Tag::{BlockQuote, CodeBlock, FootnoteDefinition, Heading, Item, Link, Paragraph}; -use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options, TagEnd}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::{Attribute, ImplItemKind, ItemKind, Node, Safety, TraitItemKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; +use rustc_resolve::rustdoc::pulldown_cmark::Event::{ + Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start, + TaskListMarker, Text, +}; +use rustc_resolve::rustdoc::pulldown_cmark::Tag::{ + BlockQuote, CodeBlock, FootnoteDefinition, Heading, Item, Link, Paragraph, +}; +use rustc_resolve::rustdoc::pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options, TagEnd}; use rustc_resolve::rustdoc::{ - DocFragment, add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range, - span_of_fragments, + DocFragment, add_doc_fragment, attrs_to_doc_fragments, main_body_opts, pulldown_cmark, + source_span_for_markdown_range, span_of_fragments, }; use rustc_session::impl_lint_pass; use rustc_span::Span; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 815411348aa66..dcc2d985f3fe9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -27,9 +27,6 @@ rustc::internal )] -// FIXME: switch to something more ergonomic here, once available. -// (Currently there is no way to opt into sysroot crates without `extern crate`.) -extern crate pulldown_cmark; extern crate rustc_abi; extern crate rustc_arena; extern crate rustc_ast; @@ -53,8 +50,6 @@ extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; extern crate rustc_trait_selection; -extern crate smallvec; -extern crate thin_vec; #[macro_use] extern crate clippy_utils; diff --git a/clippy_lints/src/methods/ip_constant.rs b/clippy_lints/src/methods/ip_constant.rs index bf602811009a2..d5e4dac5e4526 100644 --- a/clippy_lints/src/methods/ip_constant.rs +++ b/clippy_lints/src/methods/ip_constant.rs @@ -1,10 +1,10 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; +use rustc_data_structures::smallvec::SmallVec; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::sym; -use smallvec::SmallVec; use super::IP_CONSTANT; diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index f3410c98973f5..568544ad5cf1a 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -9,6 +9,7 @@ use rustc_ast::PatKind::*; use rustc_ast::mut_visit::*; use rustc_ast::{self as ast, DUMMY_NODE_ID, Mutability, Pat, PatKind}; use rustc_ast_pretty::pprust; +use rustc_data_structures::thin_vec::{ThinVec, thin_vec}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::impl_lint_pass; @@ -17,7 +18,6 @@ use rustc_span::DUMMY_SP; use std::boxed::Box; use std::cell::Cell; use std::mem; -use thin_vec::{ThinVec, thin_vec}; declare_clippy_lint! { /// ### What it does diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 24864e8ef96dc..2218c5e4e44f0 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -25,7 +25,6 @@ // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) -extern crate indexmap; extern crate rustc_abi; extern crate rustc_ast; extern crate rustc_attr_parsing; @@ -47,7 +46,6 @@ extern crate rustc_mir_dataflow; extern crate rustc_session; extern crate rustc_span; extern crate rustc_trait_selection; -extern crate smallvec; pub mod ast_utils; pub mod attrs; @@ -90,6 +88,7 @@ use rustc_abi::Integer; use rustc_ast::ast::{self, LitKind, RangeLimits}; use rustc_ast::join_path_syms; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::indexmap; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnindexMap; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 62041fc631c04..f7a0c3e39afd0 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -2,12 +2,12 @@ use crate::sym; use rustc_ast::Attribute; use rustc_ast::attr::AttributeExt; use rustc_attr_parsing::parse_version; +use rustc_data_structures::smallvec::SmallVec; use rustc_hir::RustcVersion; use rustc_lint::LateContext; use rustc_session::Session; use rustc_span::Symbol; use serde::Deserialize; -use smallvec::SmallVec; use std::iter::once; use std::sync::atomic::{AtomicBool, Ordering}; @@ -192,12 +192,12 @@ fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt]) -> Option Option Date: Thu, 16 Oct 2025 15:57:16 +0200 Subject: [PATCH 88/89] Bump nightly version -> 2025-10-16 --- clippy_utils/README.md | 2 +- rust-toolchain.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 1f678a6a29f08..9d12b46b9546f 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-10-06 +nightly-2025-10-16 ``` diff --git a/rust-toolchain.toml b/rust-toolchain.toml index e936f5dc3b7a5..d5d96448a97d3 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-10-06" +channel = "nightly-2025-10-16" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" From 4b0cfb6a83b8383a4398a20469fc0ed18bec34fa Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 16 Oct 2025 20:38:32 +0200 Subject: [PATCH 89/89] Update Cargo.lock --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index 018d03ed26c96..b358b21a5f985 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -645,6 +645,7 @@ version = "0.0.1" dependencies = [ "clippy_config", "clippy_utils", + "itertools", "regex", "rustc-semver", ]