about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJason Newcomb <jsnewcomb@pm.me>2023-09-15 07:07:34 -0400
committerJason Newcomb <jsnewcomb@pm.me>2023-09-17 09:48:12 -0400
commit79247d95f7ec00019cc26db20a07d4f1c1f335aa (patch)
treeea8d34cf0ce59ed5f911cafbf204a7dcf9bf6610
parentfed036a57c96d91ac632f0bd40a27ef78b5b96c6 (diff)
downloadrust-79247d95f7ec00019cc26db20a07d4f1c1f335aa.tar.gz
rust-79247d95f7ec00019cc26db20a07d4f1c1f335aa.zip
Split part of `needless_borrow` into `needless_borrows_for_generic_args`
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/declared_lints.rs1
-rw-r--r--clippy_lints/src/dereference.rs371
-rw-r--r--clippy_lints/src/lib.rs8
-rw-r--r--clippy_lints/src/needless_borrows_for_generic_args.rs410
-rw-r--r--tests/ui/needless_borrow.fixed290
-rw-r--r--tests/ui/needless_borrow.rs290
-rw-r--r--tests/ui/needless_borrow.stderr90
-rw-r--r--tests/ui/needless_borrows_for_generic_args.fixed287
-rw-r--r--tests/ui/needless_borrows_for_generic_args.rs287
-rw-r--r--tests/ui/needless_borrows_for_generic_args.stderr77
-rw-r--r--tests/ui/regex.rs3
-rw-r--r--tests/ui/regex.stderr48
-rw-r--r--tests/ui/unnecessary_to_owned.fixed2
-rw-r--r--tests/ui/unnecessary_to_owned.rs2
15 files changed, 1106 insertions, 1061 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9aca13a6cf6..3717b15ac5c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5171,6 +5171,7 @@ Released 2018-09-13
 [`needless_bool_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool_assign
 [`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
 [`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
+[`needless_borrows_for_generic_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrows_for_generic_args
 [`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
 [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
 [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index ecf44e05920..c4d5bee389e 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -479,6 +479,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::needless_bool::NEEDLESS_BOOL_INFO,
     crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO,
     crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO,
+    crate::needless_borrows_for_generic_args::NEEDLESS_BORROWS_FOR_GENERIC_ARGS_INFO,
     crate::needless_continue::NEEDLESS_CONTINUE_INFO,
     crate::needless_else::NEEDLESS_ELSE_INFO,
     crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,
diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs
index d0a898adff4..aa80d60a4b1 100644
--- a/clippy_lints/src/dereference.rs
+++ b/clippy_lints/src/dereference.rs
@@ -1,40 +1,24 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
-use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap};
-use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::sugg::has_enclosing_paren;
-use clippy_utils::ty::{implements_trait, is_copy, peel_mid_ty_refs};
+use clippy_utils::ty::{implements_trait, peel_mid_ty_refs};
 use clippy_utils::{
     expr_use_ctxt, get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode,
 };
-
-use hir::def::DefKind;
-use hir::MatchSource;
 use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::Applicability;
-use rustc_hir::def::Res;
-use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::{walk_ty, Visitor};
 use rustc_hir::{
-    self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind,
-    Path, QPath, TyKind, UnOp,
+    self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node,
+    Pat, PatKind, Path, QPath, TyKind, UnOp,
 };
-use rustc_index::bit_set::BitSet;
-use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::mir::{Rvalue, StatementKind};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
-use rustc_middle::ty::{
-    self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, List, ParamEnv, ParamTy, ProjectionPredicate, Ty,
-    TyCtxt, TypeVisitableExt, TypeckResults,
-};
+use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeVisitableExt, TypeckResults};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol::sym;
 use rustc_span::{Span, Symbol};
-use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
-use rustc_trait_selection::traits::{Obligation, ObligationCause};
-use std::collections::VecDeque;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -182,24 +166,6 @@ pub struct Dereferencing<'tcx> {
     ///
     /// e.g. `m!(x) | Foo::Bar(ref x)`
     ref_locals: FxIndexMap<HirId, Option<RefPat>>,
-
-    /// Stack of (body owner, `PossibleBorrowerMap`) pairs. Used by
-    /// `needless_borrow_impl_arg_position` to determine when a borrowed expression can instead
-    /// be moved.
-    possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
-
-    // `IntoIterator` for arrays requires Rust 1.53.
-    msrv: Msrv,
-}
-
-impl<'tcx> Dereferencing<'tcx> {
-    #[must_use]
-    pub fn new(msrv: Msrv) -> Self {
-        Self {
-            msrv,
-            ..Dereferencing::default()
-        }
-    }
 }
 
 #[derive(Debug)]
@@ -354,52 +320,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
                         ));
                     },
                     (Some(use_cx), RefOp::AddrOf(mutability)) => {
-                        let defined_ty = use_cx.node.defined_ty(cx);
-
-                        // Check needless_borrow for generic arguments.
-                        if !use_cx.is_ty_unified
-                            && let Some(DefinedTy::Mir(ty)) = defined_ty
-                            && let ty::Param(ty) = *ty.value.skip_binder().kind()
-                            && let Some((hir_id, fn_id, i)) = match use_cx.node {
-                                ExprUseNode::MethodArg(_, _, 0) => None,
-                                ExprUseNode::MethodArg(hir_id, None, i) => {
-                                    typeck.type_dependent_def_id(hir_id).map(|id| (hir_id, id, i))
-                                },
-                                ExprUseNode::FnArg(&Expr { kind: ExprKind::Path(ref p), hir_id, .. }, i)
-                                if !path_has_args(p) => match typeck.qpath_res(p, hir_id) {
-                                    Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) => {
-                                        Some((hir_id, id, i))
-                                    },
-                                    _ => None,
-                                },
-                                _ => None,
-                            } && let count = needless_borrow_generic_arg_count(
-                                cx,
-                                &mut self.possible_borrowers,
-                                fn_id,
-                                typeck.node_args(hir_id),
-                                i,
-                                ty,
-                                expr,
-                                &self.msrv,
-                            ) && count != 0
-                        {
-                            self.state = Some((
-                                State::DerefedBorrow(DerefedBorrow {
-                                    count: count - 1,
-                                    msg: "the borrowed expression implements the required traits",
-                                    stability: TyCoercionStability::None,
-                                    for_field_access: None,
-                                }),
-                                StateData {
-                                    span: expr.span,
-                                    hir_id: expr.hir_id,
-                                    adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target),
-                                },
-                            ));
-                            return;
-                        }
-
                         // Find the number of times the borrow is auto-derefed.
                         let mut iter = use_cx.adjustments.iter();
                         let mut deref_count = 0usize;
@@ -418,7 +338,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
                             };
                         };
 
-                        let stability = defined_ty.map_or(TyCoercionStability::None, |ty| {
+                        let stability = use_cx.node.defined_ty(cx).map_or(TyCoercionStability::None, |ty| {
                             TyCoercionStability::for_defined_ty(cx, ty, use_cx.node.is_return())
                         });
                         let can_auto_borrow = match use_cx.node {
@@ -699,12 +619,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
     }
 
     fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
-        if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| {
-            local_def_id == cx.tcx.hir().body_owner_def_id(body.id())
-        }) {
-            self.possible_borrowers.pop();
-        }
-
         if Some(body.id()) == self.current_body {
             for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
                 let replacements = pat.replacements;
@@ -728,8 +642,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
             self.current_body = None;
         }
     }
-
-    extract_msrv_attr!(LateContext);
 }
 
 fn try_parse_ref_op<'tcx>(
@@ -787,13 +699,6 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
     }
 }
 
-fn path_has_args(p: &QPath<'_>) -> bool {
-    match *p {
-        QPath::Resolved(_, Path { segments: [.., s], .. }) | QPath::TypeRelative(_, s) => s.args.is_some(),
-        _ => false,
-    }
-}
-
 fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
     if let Some(parent) = get_parent_expr(cx, e)
         && parent.span.ctxt() == e.span.ctxt()
@@ -980,272 +885,6 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
     v.0
 }
 
-/// Checks for the number of borrow expressions which can be removed from the given expression
-/// where the expression is used as an argument to a function expecting a generic type.
-///
-/// The following constraints will be checked:
-/// * The borrowed expression meets all the generic type's constraints.
-/// * The generic type appears only once in the functions signature.
-/// * The borrowed value will not be moved if it is used later in the function.
-#[expect(clippy::too_many_arguments)]
-fn needless_borrow_generic_arg_count<'tcx>(
-    cx: &LateContext<'tcx>,
-    possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
-    fn_id: DefId,
-    callee_args: &'tcx List<GenericArg<'tcx>>,
-    arg_index: usize,
-    param_ty: ParamTy,
-    mut expr: &Expr<'tcx>,
-    msrv: &Msrv,
-) -> usize {
-    let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
-    let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
-
-    let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder();
-    let predicates = cx.tcx.param_env(fn_id).caller_bounds();
-    let projection_predicates = predicates
-        .iter()
-        .filter_map(|predicate| {
-            if let ClauseKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
-                Some(projection_predicate)
-            } else {
-                None
-            }
-        })
-        .collect::<Vec<_>>();
-
-    let mut trait_with_ref_mut_self_method = false;
-
-    // If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return.
-    if predicates
-        .iter()
-        .filter_map(|predicate| {
-            if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
-                && trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx)
-            {
-                Some(trait_predicate.trait_ref.def_id)
-            } else {
-                None
-            }
-        })
-        .inspect(|trait_def_id| {
-            trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id);
-        })
-        .all(|trait_def_id| {
-            Some(trait_def_id) == destruct_trait_def_id
-                || Some(trait_def_id) == sized_trait_def_id
-                || cx.tcx.is_diagnostic_item(sym::Any, trait_def_id)
-        })
-    {
-        return 0;
-    }
-
-    // See:
-    // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201
-    // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232
-    if projection_predicates
-        .iter()
-        .any(|projection_predicate| is_mixed_projection_predicate(cx, fn_id, projection_predicate))
-    {
-        return 0;
-    }
-
-    // `args_with_referent_ty` can be constructed outside of `check_referent` because the same
-    // elements are modified each time `check_referent` is called.
-    let mut args_with_referent_ty = callee_args.to_vec();
-
-    let mut check_reference_and_referent = |reference, referent| {
-        let referent_ty = cx.typeck_results().expr_ty(referent);
-
-        if !is_copy(cx, referent_ty)
-            && (referent_ty.has_significant_drop(cx.tcx, cx.param_env)
-                || !referent_used_exactly_once(cx, possible_borrowers, reference))
-        {
-            return false;
-        }
-
-        // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
-        if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
-            return false;
-        }
-
-        if !replace_types(
-            cx,
-            param_ty,
-            referent_ty,
-            fn_sig,
-            arg_index,
-            &projection_predicates,
-            &mut args_with_referent_ty,
-        ) {
-            return false;
-        }
-
-        predicates.iter().all(|predicate| {
-            if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
-                && cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
-                && let ty::Param(param_ty) = trait_predicate.self_ty().kind()
-                && let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].unpack()
-                && ty.is_array()
-                && !msrv.meets(msrvs::ARRAY_INTO_ITERATOR)
-            {
-                return false;
-            }
-
-            let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, &args_with_referent_ty);
-            let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate);
-            let infcx = cx.tcx.infer_ctxt().build();
-            infcx.predicate_must_hold_modulo_regions(&obligation)
-        })
-    };
-
-    let mut count = 0;
-    while let ExprKind::AddrOf(_, _, referent) = expr.kind {
-        if !check_reference_and_referent(expr, referent) {
-            break;
-        }
-        expr = referent;
-        count += 1;
-    }
-    count
-}
-
-fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
-    cx.tcx
-        .associated_items(trait_def_id)
-        .in_definition_order()
-        .any(|assoc_item| {
-            if assoc_item.fn_has_self_parameter {
-                let self_ty = cx
-                    .tcx
-                    .fn_sig(assoc_item.def_id)
-                    .instantiate_identity()
-                    .skip_binder()
-                    .inputs()[0];
-                matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut))
-            } else {
-                false
-            }
-        })
-}
-
-fn is_mixed_projection_predicate<'tcx>(
-    cx: &LateContext<'tcx>,
-    callee_def_id: DefId,
-    projection_predicate: &ProjectionPredicate<'tcx>,
-) -> bool {
-    let generics = cx.tcx.generics_of(callee_def_id);
-    // The predicate requires the projected type to equal a type parameter from the parent context.
-    if let Some(term_ty) = projection_predicate.term.ty()
-        && let ty::Param(term_param_ty) = term_ty.kind()
-        && (term_param_ty.index as usize) < generics.parent_count
-    {
-        // The inner-most self type is a type parameter from the current function.
-        let mut projection_ty = projection_predicate.projection_ty;
-        loop {
-            match projection_ty.self_ty().kind() {
-                ty::Alias(ty::Projection, inner_projection_ty) => {
-                    projection_ty = *inner_projection_ty;
-                }
-                ty::Param(param_ty) => {
-                    return (param_ty.index as usize) >= generics.parent_count;
-                }
-                _ => {
-                    return false;
-                }
-            }
-        }
-    } else {
-        false
-    }
-}
-
-fn referent_used_exactly_once<'tcx>(
-    cx: &LateContext<'tcx>,
-    possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
-    reference: &Expr<'tcx>,
-) -> bool {
-    if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id)
-        && let Some(local) = expr_local(cx.tcx, reference)
-        && let [location] = *local_assignments(mir, local).as_slice()
-        && let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index)
-        && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind
-        && !place.is_indirect_first_projection()
-    {
-        let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id);
-        if possible_borrowers
-            .last()
-            .map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id)
-        {
-            possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir)));
-        }
-        let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1;
-        // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
-        // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
-        // itself. See the comment in that method for an explanation as to why.
-        possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location)
-            && used_exactly_once(mir, place.local).unwrap_or(false)
-    } else {
-        false
-    }
-}
-
-// Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting
-// projected type that is a type parameter. Returns `false` if replacing the types would have an
-// effect on the function signature beyond substituting `new_ty` for `param_ty`.
-// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757
-fn replace_types<'tcx>(
-    cx: &LateContext<'tcx>,
-    param_ty: ParamTy,
-    new_ty: Ty<'tcx>,
-    fn_sig: FnSig<'tcx>,
-    arg_index: usize,
-    projection_predicates: &[ProjectionPredicate<'tcx>],
-    args: &mut [ty::GenericArg<'tcx>],
-) -> bool {
-    let mut replaced = BitSet::new_empty(args.len());
-
-    let mut deque = VecDeque::with_capacity(args.len());
-    deque.push_back((param_ty, new_ty));
-
-    while let Some((param_ty, new_ty)) = deque.pop_front() {
-        // If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in.
-        if !fn_sig
-            .inputs_and_output
-            .iter()
-            .enumerate()
-            .all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx)))
-        {
-            return false;
-        }
-
-        args[param_ty.index as usize] = ty::GenericArg::from(new_ty);
-
-        // The `replaced.insert(...)` check provides some protection against infinite loops.
-        if replaced.insert(param_ty.index) {
-            for projection_predicate in projection_predicates {
-                if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx)
-                    && let Some(term_ty) = projection_predicate.term.ty()
-                    && let ty::Param(term_param_ty) = term_ty.kind()
-                {
-                    let projection = cx.tcx.mk_ty_from_kind(ty::Alias(
-                        ty::Projection,
-                        projection_predicate.projection_ty.with_self_ty(cx.tcx, new_ty),
-                    ));
-
-                    if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
-                        && args[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
-                    {
-                        deque.push_back((*term_param_ty, projected_ty));
-                    }
-                }
-            }
-        }
-    }
-
-    true
-}
-
 fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
     if let ty::Adt(adt, _) = *ty.kind() {
         adt.is_struct() && adt.all_fields().any(|f| f.name == name)
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 3f6bdb2b62c..614ddcda88f 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -228,6 +228,7 @@ mod mutex_atomic;
 mod needless_arbitrary_self_type;
 mod needless_bool;
 mod needless_borrowed_ref;
+mod needless_borrows_for_generic_args;
 mod needless_continue;
 mod needless_else;
 mod needless_for_each;
@@ -880,7 +881,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
     store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default());
     store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress));
-    store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv())));
+    store.register_late_pass(|_| Box::<dereference::Dereferencing<'_>>::default());
     store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse));
     store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
     let future_size_threshold = conf.future_size_threshold;
@@ -1104,6 +1105,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls));
     store.register_late_pass(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing));
     store.register_late_pass(|_| Box::new(unnecessary_map_on_constructor::UnnecessaryMapOnConstructor));
+    store.register_late_pass(move |_| {
+        Box::new(needless_borrows_for_generic_args::NeedlessBorrowsForGenericArgs::new(
+            msrv(),
+        ))
+    });
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs
new file mode 100644
index 00000000000..d55c77a92b1
--- /dev/null
+++ b/clippy_lints/src/needless_borrows_for_generic_args.rs
@@ -0,0 +1,410 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap};
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::source::snippet_with_context;
+use clippy_utils::ty::is_copy;
+use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode};
+use rustc_errors::Applicability;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath};
+use rustc_index::bit_set::BitSet;
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::mir::{Rvalue, StatementKind};
+use rustc_middle::ty::{
+    self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, List, ParamTy, ProjectionPredicate, Ty,
+};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::symbol::sym;
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
+use rustc_trait_selection::traits::{Obligation, ObligationCause};
+use std::collections::VecDeque;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for borrow operations (`&`) that used as a generic argument to a
+    /// function when the borrowed value could be used.
+    ///
+    /// ### Why is this bad?
+    /// Suggests that the receiver of the expression borrows
+    /// the expression.
+    ///
+    /// ### Known problems
+    /// The lint cannot tell when the implementation of a trait
+    /// for `&T` and `T` do different things. Removing a borrow
+    /// in such a case can change the semantics of the code.
+    ///
+    /// ### Example
+    /// ```rust
+    /// fn f(_: impl AsRef<str>) {}
+    ///
+    /// let x = "foo";
+    /// f(&x);
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// fn f(_: impl AsRef<str>) {}
+    ///
+    /// let x = "foo";
+    /// f(x);
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub NEEDLESS_BORROWS_FOR_GENERIC_ARGS,
+    style,
+    "taking a reference that is going to be automatically dereferenced"
+}
+
+pub struct NeedlessBorrowsForGenericArgs<'tcx> {
+    /// Stack of (body owner, `PossibleBorrowerMap`) pairs. Used by
+    /// `needless_borrow_impl_arg_position` to determine when a borrowed expression can instead
+    /// be moved.
+    possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
+
+    // `IntoIterator` for arrays requires Rust 1.53.
+    msrv: Msrv,
+}
+impl_lint_pass!(NeedlessBorrowsForGenericArgs<'_> => [NEEDLESS_BORROWS_FOR_GENERIC_ARGS]);
+
+impl NeedlessBorrowsForGenericArgs<'_> {
+    #[must_use]
+    pub fn new(msrv: Msrv) -> Self {
+        Self {
+            possible_borrowers: Vec::new(),
+            msrv,
+        }
+    }
+}
+
+impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if matches!(expr.kind, ExprKind::AddrOf(..))
+            && !expr.span.from_expansion()
+            && let Some(use_cx) = expr_use_ctxt(cx, expr)
+            && !use_cx.is_ty_unified
+            && let Some(DefinedTy::Mir(ty)) = use_cx.node.defined_ty(cx)
+            && let ty::Param(ty) = *ty.value.skip_binder().kind()
+            && let Some((hir_id, fn_id, i)) = match use_cx.node {
+                ExprUseNode::MethodArg(_, _, 0) => None,
+                ExprUseNode::MethodArg(hir_id, None, i) => {
+                    cx.typeck_results().type_dependent_def_id(hir_id).map(|id| (hir_id, id, i))
+                },
+                ExprUseNode::FnArg(&Expr { kind: ExprKind::Path(ref p), hir_id, .. }, i)
+                if !path_has_args(p) => match cx.typeck_results().qpath_res(p, hir_id) {
+                    Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) => {
+                        Some((hir_id, id, i))
+                    },
+                    _ => None,
+                },
+                _ => None,
+            } && let count = needless_borrow_count(
+                cx,
+                &mut self.possible_borrowers,
+                fn_id,
+                cx.typeck_results().node_args(hir_id),
+                i,
+                ty,
+                expr,
+                &self.msrv,
+            ) && count != 0
+        {
+            span_lint_and_then(
+                cx,
+                NEEDLESS_BORROWS_FOR_GENERIC_ARGS,
+                expr.span,
+                "the borrowed expression implements the required traits",
+                |diag| {
+                    let mut app = Applicability::MachineApplicable;
+                    let snip_span = peel_n_hir_expr_refs(expr, count).0.span;
+                    let snip = snippet_with_context(cx, snip_span, expr.span.ctxt(), "..", &mut app).0;
+                    diag.span_suggestion(expr.span, "change this to", snip.into_owned(), app);
+                }
+            );
+        }
+    }
+
+    fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
+        if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| {
+            local_def_id == cx.tcx.hir().body_owner_def_id(body.id())
+        }) {
+            self.possible_borrowers.pop();
+        }
+    }
+
+    extract_msrv_attr!(LateContext);
+}
+
+fn path_has_args(p: &QPath<'_>) -> bool {
+    match *p {
+        QPath::Resolved(_, Path { segments: [.., s], .. }) | QPath::TypeRelative(_, s) => s.args.is_some(),
+        _ => false,
+    }
+}
+
+/// Checks for the number of borrow expressions which can be removed from the given expression
+/// where the expression is used as an argument to a function expecting a generic type.
+///
+/// The following constraints will be checked:
+/// * The borrowed expression meets all the generic type's constraints.
+/// * The generic type appears only once in the functions signature.
+/// * The borrowed value will not be moved if it is used later in the function.
+#[expect(clippy::too_many_arguments)]
+fn needless_borrow_count<'tcx>(
+    cx: &LateContext<'tcx>,
+    possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
+    fn_id: DefId,
+    callee_args: &'tcx List<GenericArg<'tcx>>,
+    arg_index: usize,
+    param_ty: ParamTy,
+    mut expr: &Expr<'tcx>,
+    msrv: &Msrv,
+) -> usize {
+    let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
+    let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
+
+    let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder();
+    let predicates = cx.tcx.param_env(fn_id).caller_bounds();
+    let projection_predicates = predicates
+        .iter()
+        .filter_map(|predicate| {
+            if let ClauseKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
+                Some(projection_predicate)
+            } else {
+                None
+            }
+        })
+        .collect::<Vec<_>>();
+
+    let mut trait_with_ref_mut_self_method = false;
+
+    // If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return.
+    if predicates
+        .iter()
+        .filter_map(|predicate| {
+            if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
+                && trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx)
+            {
+                Some(trait_predicate.trait_ref.def_id)
+            } else {
+                None
+            }
+        })
+        .inspect(|trait_def_id| {
+            trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id);
+        })
+        .all(|trait_def_id| {
+            Some(trait_def_id) == destruct_trait_def_id
+                || Some(trait_def_id) == sized_trait_def_id
+                || cx.tcx.is_diagnostic_item(sym::Any, trait_def_id)
+        })
+    {
+        return 0;
+    }
+
+    // See:
+    // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201
+    // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232
+    if projection_predicates
+        .iter()
+        .any(|projection_predicate| is_mixed_projection_predicate(cx, fn_id, projection_predicate))
+    {
+        return 0;
+    }
+
+    // `args_with_referent_ty` can be constructed outside of `check_referent` because the same
+    // elements are modified each time `check_referent` is called.
+    let mut args_with_referent_ty = callee_args.to_vec();
+
+    let mut check_reference_and_referent = |reference, referent| {
+        let referent_ty = cx.typeck_results().expr_ty(referent);
+
+        if !is_copy(cx, referent_ty)
+            && (referent_ty.has_significant_drop(cx.tcx, cx.param_env)
+                || !referent_used_exactly_once(cx, possible_borrowers, reference))
+        {
+            return false;
+        }
+
+        // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
+        if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
+            return false;
+        }
+
+        if !replace_types(
+            cx,
+            param_ty,
+            referent_ty,
+            fn_sig,
+            arg_index,
+            &projection_predicates,
+            &mut args_with_referent_ty,
+        ) {
+            return false;
+        }
+
+        predicates.iter().all(|predicate| {
+            if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
+                && cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
+                && let ty::Param(param_ty) = trait_predicate.self_ty().kind()
+                && let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].unpack()
+                && ty.is_array()
+                && !msrv.meets(msrvs::ARRAY_INTO_ITERATOR)
+            {
+                return false;
+            }
+
+            let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, &args_with_referent_ty);
+            let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate);
+            let infcx = cx.tcx.infer_ctxt().build();
+            infcx.predicate_must_hold_modulo_regions(&obligation)
+        })
+    };
+
+    let mut count = 0;
+    while let ExprKind::AddrOf(_, _, referent) = expr.kind {
+        if !check_reference_and_referent(expr, referent) {
+            break;
+        }
+        expr = referent;
+        count += 1;
+    }
+    count
+}
+
+fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
+    cx.tcx
+        .associated_items(trait_def_id)
+        .in_definition_order()
+        .any(|assoc_item| {
+            if assoc_item.fn_has_self_parameter {
+                let self_ty = cx
+                    .tcx
+                    .fn_sig(assoc_item.def_id)
+                    .instantiate_identity()
+                    .skip_binder()
+                    .inputs()[0];
+                matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut))
+            } else {
+                false
+            }
+        })
+}
+
+fn is_mixed_projection_predicate<'tcx>(
+    cx: &LateContext<'tcx>,
+    callee_def_id: DefId,
+    projection_predicate: &ProjectionPredicate<'tcx>,
+) -> bool {
+    let generics = cx.tcx.generics_of(callee_def_id);
+    // The predicate requires the projected type to equal a type parameter from the parent context.
+    if let Some(term_ty) = projection_predicate.term.ty()
+        && let ty::Param(term_param_ty) = term_ty.kind()
+        && (term_param_ty.index as usize) < generics.parent_count
+    {
+        // The inner-most self type is a type parameter from the current function.
+        let mut projection_ty = projection_predicate.projection_ty;
+        loop {
+            match projection_ty.self_ty().kind() {
+                ty::Alias(ty::Projection, inner_projection_ty) => {
+                    projection_ty = *inner_projection_ty;
+                }
+                ty::Param(param_ty) => {
+                    return (param_ty.index as usize) >= generics.parent_count;
+                }
+                _ => {
+                    return false;
+                }
+            }
+        }
+    } else {
+        false
+    }
+}
+
+fn referent_used_exactly_once<'tcx>(
+    cx: &LateContext<'tcx>,
+    possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
+    reference: &Expr<'tcx>,
+) -> bool {
+    if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id)
+        && let Some(local) = expr_local(cx.tcx, reference)
+        && let [location] = *local_assignments(mir, local).as_slice()
+        && let block_data = &mir.basic_blocks[location.block]
+        && let Some(statement) = block_data.statements.get(location.statement_index)
+        && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind
+        && !place.is_indirect_first_projection()
+    {
+        let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id);
+        if possible_borrowers
+            .last()
+            .map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id)
+        {
+            possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir)));
+        }
+        let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1;
+        // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
+        // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
+        // itself. See the comment in that method for an explanation as to why.
+        possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location)
+            && used_exactly_once(mir, place.local).unwrap_or(false)
+    } else {
+        false
+    }
+}
+
+// Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting
+// projected type that is a type parameter. Returns `false` if replacing the types would have an
+// effect on the function signature beyond substituting `new_ty` for `param_ty`.
+// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757
+fn replace_types<'tcx>(
+    cx: &LateContext<'tcx>,
+    param_ty: ParamTy,
+    new_ty: Ty<'tcx>,
+    fn_sig: FnSig<'tcx>,
+    arg_index: usize,
+    projection_predicates: &[ProjectionPredicate<'tcx>],
+    args: &mut [ty::GenericArg<'tcx>],
+) -> bool {
+    let mut replaced = BitSet::new_empty(args.len());
+
+    let mut deque = VecDeque::with_capacity(args.len());
+    deque.push_back((param_ty, new_ty));
+
+    while let Some((param_ty, new_ty)) = deque.pop_front() {
+        // If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in.
+        if !fn_sig
+            .inputs_and_output
+            .iter()
+            .enumerate()
+            .all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx)))
+        {
+            return false;
+        }
+
+        args[param_ty.index as usize] = ty::GenericArg::from(new_ty);
+
+        // The `replaced.insert(...)` check provides some protection against infinite loops.
+        if replaced.insert(param_ty.index) {
+            for projection_predicate in projection_predicates {
+                if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx)
+                    && let Some(term_ty) = projection_predicate.term.ty()
+                    && let ty::Param(term_param_ty) = term_ty.kind()
+                {
+                    let projection = cx.tcx.mk_ty_from_kind(ty::Alias(
+                        ty::Projection,
+                        projection_predicate.projection_ty.with_self_ty(cx.tcx, new_ty),
+                    ));
+
+                    if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
+                        && args[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
+                    {
+                        deque.push_back((*term_param_ty, projected_ty));
+                    }
+                }
+            }
+        }
+    }
+
+    true
+}
diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed
index 0a52b25229d..c2c5f765abf 100644
--- a/tests/ui/needless_borrow.fixed
+++ b/tests/ui/needless_borrow.fixed
@@ -131,21 +131,6 @@ fn main() {
             0
         }
     }
-
-    let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap();
-    let _ = std::path::Path::new(".").join(".");
-    deref_target_is_x(X);
-    multiple_constraints([[""]]);
-    multiple_constraints_normalizes_to_same(X, X);
-    let _ = Some("").unwrap_or("");
-    let _ = std::fs::write("x", "".to_string());
-
-    only_sized(&""); // Don't lint. `Sized` is only bound
-    let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound
-    let _ = Box::new(&""); // Don't lint. Type parameter appears in return type
-    ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter
-    refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't
-    multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments
 }
 
 #[allow(clippy::needless_borrowed_reference)]
@@ -201,103 +186,6 @@ mod issue9160 {
     }
 }
 
-#[derive(Clone, Copy)]
-struct X;
-
-impl std::ops::Deref for X {
-    type Target = X;
-    fn deref(&self) -> &Self::Target {
-        self
-    }
-}
-
-fn deref_target_is_x<T>(_: T)
-where
-    T: std::ops::Deref<Target = X>,
-{
-}
-
-fn multiple_constraints<T, U, V, X, Y>(_: T)
-where
-    T: IntoIterator<Item = U> + IntoIterator<Item = X>,
-    U: IntoIterator<Item = V>,
-    V: AsRef<str>,
-    X: IntoIterator<Item = Y>,
-    Y: AsRef<std::ffi::OsStr>,
-{
-}
-
-fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V)
-where
-    T: std::ops::Deref<Target = U>,
-    U: std::ops::Deref<Target = V>,
-{
-}
-
-fn only_sized<T>(_: T) {}
-
-fn ref_as_ref_path<T: 'static>(_: &'static T)
-where
-    &'static T: AsRef<std::path::Path>,
-{
-}
-
-trait RefsOnly {
-    type Referent;
-}
-
-impl<T> RefsOnly for &T {
-    type Referent = T;
-}
-
-fn refs_only<T, U>(_: T)
-where
-    T: RefsOnly<Referent = U>,
-{
-}
-
-fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U)
-where
-    T: IntoIterator<Item = U>,
-    U: IntoIterator<Item = V>,
-    V: AsRef<str>,
-{
-}
-
-// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
-mod copyable_iterator {
-    #[derive(Clone, Copy)]
-    struct Iter;
-    impl Iterator for Iter {
-        type Item = ();
-        fn next(&mut self) -> Option<Self::Item> {
-            None
-        }
-    }
-    fn takes_iter(_: impl Iterator) {}
-    fn dont_warn(mut x: Iter) {
-        takes_iter(&mut x);
-    }
-    #[allow(unused_mut)]
-    fn warn(mut x: &mut Iter) {
-        takes_iter(x)
-    }
-}
-
-#[clippy::msrv = "1.52.0"]
-mod under_msrv {
-    fn foo() {
-        let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
-    }
-}
-
-#[clippy::msrv = "1.53.0"]
-mod meets_msrv {
-    fn foo() {
-        let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap();
-    }
-}
-
 fn issue9383() {
     // Should not lint because unions need explicit deref when accessing field
     use std::mem::ManuallyDrop;
@@ -326,184 +214,6 @@ fn issue9383() {
     }
 }
 
-fn closure_test() {
-    let env = "env".to_owned();
-    let arg = "arg".to_owned();
-    let f = |arg| {
-        let loc = "loc".to_owned();
-        let _ = std::fs::write("x", &env); // Don't lint. In environment
-        let _ = std::fs::write("x", arg);
-        let _ = std::fs::write("x", loc);
-    };
-    let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f`
-    f(arg);
-}
-
-mod significant_drop {
-    #[derive(Debug)]
-    struct X;
-
-    #[derive(Debug)]
-    struct Y;
-
-    impl Drop for Y {
-        fn drop(&mut self) {}
-    }
-
-    fn foo(x: X, y: Y) {
-        debug(x);
-        debug(&y); // Don't lint. Has significant drop
-    }
-
-    fn debug(_: impl std::fmt::Debug) {}
-}
-
-mod used_exactly_once {
-    fn foo(x: String) {
-        use_x(x);
-    }
-    fn use_x(_: impl AsRef<str>) {}
-}
-
-mod used_more_than_once {
-    fn foo(x: String) {
-        use_x(&x);
-        use_x_again(&x);
-    }
-    fn use_x(_: impl AsRef<str>) {}
-    fn use_x_again(_: impl AsRef<str>) {}
-}
-
-// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280
-mod issue_9111 {
-    struct A;
-
-    impl Extend<u8> for A {
-        fn extend<T: IntoIterator<Item = u8>>(&mut self, _: T) {
-            unimplemented!()
-        }
-    }
-
-    impl<'a> Extend<&'a u8> for A {
-        fn extend<T: IntoIterator<Item = &'a u8>>(&mut self, _: T) {
-            unimplemented!()
-        }
-    }
-
-    fn main() {
-        let mut a = A;
-        a.extend(&[]); // vs a.extend([]);
-    }
-}
-
-mod issue_9710 {
-    fn main() {
-        let string = String::new();
-        for _i in 0..10 {
-            f(&string);
-        }
-    }
-
-    fn f<T: AsRef<str>>(_: T) {}
-}
-
-mod issue_9739 {
-    fn foo<D: std::fmt::Display>(_it: impl IntoIterator<Item = D>) {}
-
-    fn main() {
-        foo(if std::env::var_os("HI").is_some() {
-            &[0]
-        } else {
-            &[] as &[u32]
-        });
-    }
-}
-
-mod issue_9739_method_variant {
-    struct S;
-
-    impl S {
-        fn foo<D: std::fmt::Display>(&self, _it: impl IntoIterator<Item = D>) {}
-    }
-
-    fn main() {
-        S.foo(if std::env::var_os("HI").is_some() {
-            &[0]
-        } else {
-            &[] as &[u32]
-        });
-    }
-}
-
-mod issue_9782 {
-    fn foo<T: AsRef<[u8]>>(t: T) {
-        println!("{}", std::mem::size_of::<T>());
-        let _t: &[u8] = t.as_ref();
-    }
-
-    fn main() {
-        let a: [u8; 100] = [0u8; 100];
-
-        // 100
-        foo::<[u8; 100]>(a);
-        foo(a);
-
-        // 16
-        foo::<&[u8]>(&a);
-        foo(a.as_slice());
-
-        // 8
-        foo::<&[u8; 100]>(&a);
-        foo(a);
-    }
-}
-
-mod issue_9782_type_relative_variant {
-    struct S;
-
-    impl S {
-        fn foo<T: AsRef<[u8]>>(t: T) {
-            println!("{}", std::mem::size_of::<T>());
-            let _t: &[u8] = t.as_ref();
-        }
-    }
-
-    fn main() {
-        let a: [u8; 100] = [0u8; 100];
-
-        S::foo::<&[u8; 100]>(&a);
-    }
-}
-
-mod issue_9782_method_variant {
-    struct S;
-
-    impl S {
-        fn foo<T: AsRef<[u8]>>(&self, t: T) {
-            println!("{}", std::mem::size_of::<T>());
-            let _t: &[u8] = t.as_ref();
-        }
-    }
-
-    fn main() {
-        let a: [u8; 100] = [0u8; 100];
-
-        S.foo::<&[u8; 100]>(&a);
-    }
-}
-
-mod issue_10535 {
-    static SOME_STATIC: String = String::new();
-
-    static UNIT: () = compute(&SOME_STATIC);
-
-    pub const fn compute<T>(_: T)
-    where
-        T: Copy,
-    {
-    }
-}
-
 mod issue_10253 {
     struct S;
     trait X {
diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs
index 34a95d18463..0cd6e41b8a4 100644
--- a/tests/ui/needless_borrow.rs
+++ b/tests/ui/needless_borrow.rs
@@ -131,21 +131,6 @@ fn main() {
             0
         }
     }
-
-    let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
-    let _ = std::path::Path::new(".").join(&&".");
-    deref_target_is_x(&X);
-    multiple_constraints(&[[""]]);
-    multiple_constraints_normalizes_to_same(&X, X);
-    let _ = Some("").unwrap_or(&"");
-    let _ = std::fs::write("x", &"".to_string());
-
-    only_sized(&""); // Don't lint. `Sized` is only bound
-    let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound
-    let _ = Box::new(&""); // Don't lint. Type parameter appears in return type
-    ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter
-    refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't
-    multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments
 }
 
 #[allow(clippy::needless_borrowed_reference)]
@@ -201,103 +186,6 @@ mod issue9160 {
     }
 }
 
-#[derive(Clone, Copy)]
-struct X;
-
-impl std::ops::Deref for X {
-    type Target = X;
-    fn deref(&self) -> &Self::Target {
-        self
-    }
-}
-
-fn deref_target_is_x<T>(_: T)
-where
-    T: std::ops::Deref<Target = X>,
-{
-}
-
-fn multiple_constraints<T, U, V, X, Y>(_: T)
-where
-    T: IntoIterator<Item = U> + IntoIterator<Item = X>,
-    U: IntoIterator<Item = V>,
-    V: AsRef<str>,
-    X: IntoIterator<Item = Y>,
-    Y: AsRef<std::ffi::OsStr>,
-{
-}
-
-fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V)
-where
-    T: std::ops::Deref<Target = U>,
-    U: std::ops::Deref<Target = V>,
-{
-}
-
-fn only_sized<T>(_: T) {}
-
-fn ref_as_ref_path<T: 'static>(_: &'static T)
-where
-    &'static T: AsRef<std::path::Path>,
-{
-}
-
-trait RefsOnly {
-    type Referent;
-}
-
-impl<T> RefsOnly for &T {
-    type Referent = T;
-}
-
-fn refs_only<T, U>(_: T)
-where
-    T: RefsOnly<Referent = U>,
-{
-}
-
-fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U)
-where
-    T: IntoIterator<Item = U>,
-    U: IntoIterator<Item = V>,
-    V: AsRef<str>,
-{
-}
-
-// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
-mod copyable_iterator {
-    #[derive(Clone, Copy)]
-    struct Iter;
-    impl Iterator for Iter {
-        type Item = ();
-        fn next(&mut self) -> Option<Self::Item> {
-            None
-        }
-    }
-    fn takes_iter(_: impl Iterator) {}
-    fn dont_warn(mut x: Iter) {
-        takes_iter(&mut x);
-    }
-    #[allow(unused_mut)]
-    fn warn(mut x: &mut Iter) {
-        takes_iter(&mut x)
-    }
-}
-
-#[clippy::msrv = "1.52.0"]
-mod under_msrv {
-    fn foo() {
-        let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
-    }
-}
-
-#[clippy::msrv = "1.53.0"]
-mod meets_msrv {
-    fn foo() {
-        let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
-    }
-}
-
 fn issue9383() {
     // Should not lint because unions need explicit deref when accessing field
     use std::mem::ManuallyDrop;
@@ -326,184 +214,6 @@ fn issue9383() {
     }
 }
 
-fn closure_test() {
-    let env = "env".to_owned();
-    let arg = "arg".to_owned();
-    let f = |arg| {
-        let loc = "loc".to_owned();
-        let _ = std::fs::write("x", &env); // Don't lint. In environment
-        let _ = std::fs::write("x", &arg);
-        let _ = std::fs::write("x", &loc);
-    };
-    let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f`
-    f(arg);
-}
-
-mod significant_drop {
-    #[derive(Debug)]
-    struct X;
-
-    #[derive(Debug)]
-    struct Y;
-
-    impl Drop for Y {
-        fn drop(&mut self) {}
-    }
-
-    fn foo(x: X, y: Y) {
-        debug(&x);
-        debug(&y); // Don't lint. Has significant drop
-    }
-
-    fn debug(_: impl std::fmt::Debug) {}
-}
-
-mod used_exactly_once {
-    fn foo(x: String) {
-        use_x(&x);
-    }
-    fn use_x(_: impl AsRef<str>) {}
-}
-
-mod used_more_than_once {
-    fn foo(x: String) {
-        use_x(&x);
-        use_x_again(&x);
-    }
-    fn use_x(_: impl AsRef<str>) {}
-    fn use_x_again(_: impl AsRef<str>) {}
-}
-
-// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280
-mod issue_9111 {
-    struct A;
-
-    impl Extend<u8> for A {
-        fn extend<T: IntoIterator<Item = u8>>(&mut self, _: T) {
-            unimplemented!()
-        }
-    }
-
-    impl<'a> Extend<&'a u8> for A {
-        fn extend<T: IntoIterator<Item = &'a u8>>(&mut self, _: T) {
-            unimplemented!()
-        }
-    }
-
-    fn main() {
-        let mut a = A;
-        a.extend(&[]); // vs a.extend([]);
-    }
-}
-
-mod issue_9710 {
-    fn main() {
-        let string = String::new();
-        for _i in 0..10 {
-            f(&string);
-        }
-    }
-
-    fn f<T: AsRef<str>>(_: T) {}
-}
-
-mod issue_9739 {
-    fn foo<D: std::fmt::Display>(_it: impl IntoIterator<Item = D>) {}
-
-    fn main() {
-        foo(if std::env::var_os("HI").is_some() {
-            &[0]
-        } else {
-            &[] as &[u32]
-        });
-    }
-}
-
-mod issue_9739_method_variant {
-    struct S;
-
-    impl S {
-        fn foo<D: std::fmt::Display>(&self, _it: impl IntoIterator<Item = D>) {}
-    }
-
-    fn main() {
-        S.foo(if std::env::var_os("HI").is_some() {
-            &[0]
-        } else {
-            &[] as &[u32]
-        });
-    }
-}
-
-mod issue_9782 {
-    fn foo<T: AsRef<[u8]>>(t: T) {
-        println!("{}", std::mem::size_of::<T>());
-        let _t: &[u8] = t.as_ref();
-    }
-
-    fn main() {
-        let a: [u8; 100] = [0u8; 100];
-
-        // 100
-        foo::<[u8; 100]>(a);
-        foo(a);
-
-        // 16
-        foo::<&[u8]>(&a);
-        foo(a.as_slice());
-
-        // 8
-        foo::<&[u8; 100]>(&a);
-        foo(&a);
-    }
-}
-
-mod issue_9782_type_relative_variant {
-    struct S;
-
-    impl S {
-        fn foo<T: AsRef<[u8]>>(t: T) {
-            println!("{}", std::mem::size_of::<T>());
-            let _t: &[u8] = t.as_ref();
-        }
-    }
-
-    fn main() {
-        let a: [u8; 100] = [0u8; 100];
-
-        S::foo::<&[u8; 100]>(&a);
-    }
-}
-
-mod issue_9782_method_variant {
-    struct S;
-
-    impl S {
-        fn foo<T: AsRef<[u8]>>(&self, t: T) {
-            println!("{}", std::mem::size_of::<T>());
-            let _t: &[u8] = t.as_ref();
-        }
-    }
-
-    fn main() {
-        let a: [u8; 100] = [0u8; 100];
-
-        S.foo::<&[u8; 100]>(&a);
-    }
-}
-
-mod issue_10535 {
-    static SOME_STATIC: String = String::new();
-
-    static UNIT: () = compute(&SOME_STATIC);
-
-    pub const fn compute<T>(_: T)
-    where
-        T: Copy,
-    {
-    }
-}
-
 mod issue_10253 {
     struct S;
     trait X {
diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr
index 8e27014d53c..e91b78b0a15 100644
--- a/tests/ui/needless_borrow.stderr
+++ b/tests/ui/needless_borrow.stderr
@@ -121,101 +121,17 @@ error: this expression creates a reference which is immediately dereferenced by
 LL |     (&&5).foo();
    |     ^^^^^ help: change this to: `(&5)`
 
-error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:135:51
-   |
-LL |     let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
-   |                                                   ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
-
-error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:136:44
-   |
-LL |     let _ = std::path::Path::new(".").join(&&".");
-   |                                            ^^^^^ help: change this to: `"."`
-
-error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:137:23
-   |
-LL |     deref_target_is_x(&X);
-   |                       ^^ help: change this to: `X`
-
-error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:138:26
-   |
-LL |     multiple_constraints(&[[""]]);
-   |                          ^^^^^^^ help: change this to: `[[""]]`
-
-error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:139:45
-   |
-LL |     multiple_constraints_normalizes_to_same(&X, X);
-   |                                             ^^ help: change this to: `X`
-
-error: this expression creates a reference which is immediately dereferenced by the compiler
-  --> $DIR/needless_borrow.rs:140:32
-   |
-LL |     let _ = Some("").unwrap_or(&"");
-   |                                ^^^ help: change this to: `""`
-
-error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:141:33
-   |
-LL |     let _ = std::fs::write("x", &"".to_string());
-   |                                 ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()`
-
 error: this expression borrows a value the compiler would automatically borrow
-  --> $DIR/needless_borrow.rs:190:13
+  --> $DIR/needless_borrow.rs:175:13
    |
 LL |             (&self.f)()
    |             ^^^^^^^^^ help: change this to: `(self.f)`
 
 error: this expression borrows a value the compiler would automatically borrow
-  --> $DIR/needless_borrow.rs:199:13
+  --> $DIR/needless_borrow.rs:184:13
    |
 LL |             (&mut self.f)()
    |             ^^^^^^^^^^^^^ help: change this to: `(self.f)`
 
-error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:283:20
-   |
-LL |         takes_iter(&mut x)
-   |                    ^^^^^^ help: change this to: `x`
-
-error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:297:55
-   |
-LL |         let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
-   |                                                       ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
-
-error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:335:37
-   |
-LL |         let _ = std::fs::write("x", &arg);
-   |                                     ^^^^ help: change this to: `arg`
-
-error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:336:37
-   |
-LL |         let _ = std::fs::write("x", &loc);
-   |                                     ^^^^ help: change this to: `loc`
-
-error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:354:15
-   |
-LL |         debug(&x);
-   |               ^^ help: change this to: `x`
-
-error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:363:15
-   |
-LL |         use_x(&x);
-   |               ^^ help: change this to: `x`
-
-error: the borrowed expression implements the required traits
-  --> $DIR/needless_borrow.rs:457:13
-   |
-LL |         foo(&a);
-   |             ^^ help: change this to: `a`
-
-error: aborting due to 36 previous errors
+error: aborting due to 22 previous errors
 
diff --git a/tests/ui/needless_borrows_for_generic_args.fixed b/tests/ui/needless_borrows_for_generic_args.fixed
new file mode 100644
index 00000000000..2a335516f51
--- /dev/null
+++ b/tests/ui/needless_borrows_for_generic_args.fixed
@@ -0,0 +1,287 @@
+#![warn(clippy::needless_borrows_for_generic_args)]
+#![allow(
+    clippy::unnecessary_to_owned,
+    clippy::unnecessary_literal_unwrap,
+    clippy::needless_borrow
+)]
+
+use core::ops::Deref;
+use std::any::Any;
+use std::ffi::OsStr;
+use std::fmt::{Debug, Display};
+use std::path::Path;
+use std::process::Command;
+
+fn main() {
+    let _ = Command::new("ls").args(["-a", "-l"]).status().unwrap();
+    let _ = Path::new(".").join(".");
+    let _ = Any::type_id(&""); // Don't lint. `Any` is only bound
+    let _ = Box::new(&""); // Don't lint. Type parameter appears in return type
+    let _ = Some("").unwrap_or(&"");
+    let _ = std::fs::write("x", "".to_string());
+
+    {
+        #[derive(Clone, Copy)]
+        struct X;
+
+        impl Deref for X {
+            type Target = X;
+            fn deref(&self) -> &Self::Target {
+                self
+            }
+        }
+
+        fn deref_target_is_x<T: Deref<Target = X>>(_: T) {}
+
+        deref_target_is_x(X);
+    }
+    {
+        fn multiple_constraints<T, U, V, X, Y>(_: T)
+        where
+            T: IntoIterator<Item = U> + IntoIterator<Item = X>,
+            U: IntoIterator<Item = V>,
+            V: AsRef<str>,
+            X: IntoIterator<Item = Y>,
+            Y: AsRef<OsStr>,
+        {
+        }
+
+        multiple_constraints([[""]]);
+    }
+    {
+        #[derive(Clone, Copy)]
+        struct X;
+
+        impl Deref for X {
+            type Target = X;
+            fn deref(&self) -> &Self::Target {
+                self
+            }
+        }
+
+        fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V)
+        where
+            T: Deref<Target = U>,
+            U: Deref<Target = V>,
+        {
+        }
+
+        multiple_constraints_normalizes_to_same(X, X);
+    }
+    {
+        fn only_sized<T>(_: T) {}
+        only_sized(&""); // Don't lint. `Sized` is only bound
+    }
+    {
+        fn ref_as_ref_path<T: 'static>(_: &'static T)
+        where
+            &'static T: AsRef<Path>,
+        {
+        }
+
+        ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter
+    }
+    {
+        trait RefsOnly {
+            type Referent;
+        }
+
+        impl<T> RefsOnly for &T {
+            type Referent = T;
+        }
+
+        fn refs_only<T, U>(_: T)
+        where
+            T: RefsOnly<Referent = U>,
+        {
+        }
+
+        refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't
+    }
+    {
+        fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U)
+        where
+            T: IntoIterator<Item = U>,
+            U: IntoIterator<Item = V>,
+            V: AsRef<str>,
+        {
+        }
+        multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments
+    }
+    // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
+    {
+        #[derive(Clone, Copy)]
+        struct Iter;
+        impl Iterator for Iter {
+            type Item = ();
+            fn next(&mut self) -> Option<Self::Item> {
+                None
+            }
+        }
+        fn takes_iter(_: impl Iterator) {}
+        fn dont_warn(mut x: Iter) {
+            takes_iter(&mut x);
+        }
+        #[allow(unused_mut)]
+        fn warn(mut x: &mut Iter) {
+            takes_iter(x)
+        }
+    }
+    #[clippy::msrv = "1.52.0"]
+    {
+        let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap();
+    };
+    #[clippy::msrv = "1.53.0"]
+    {
+        let _ = Command::new("ls").args(["-a", "-l"]).status().unwrap();
+    };
+    {
+        let env = "env".to_owned();
+        let arg = "arg".to_owned();
+        let f = |arg| {
+            let loc = "loc".to_owned();
+            let _ = std::fs::write("x", &env); // Don't lint. In environment
+            let _ = std::fs::write("x", arg);
+            let _ = std::fs::write("x", loc);
+        };
+        let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f`
+        f(arg);
+    }
+    {
+        #[derive(Debug)]
+        struct X;
+
+        impl Drop for X {
+            fn drop(&mut self) {}
+        }
+
+        fn f(_: impl Debug) {}
+
+        let x = X;
+        f(&x); // Don't lint. Has significant drop
+    }
+    {
+        fn f(_: impl AsRef<str>) {}
+
+        let x = String::new();
+        f(x);
+    }
+    {
+        fn f(_: impl AsRef<str>) {}
+        fn f2(_: impl AsRef<str>) {}
+
+        let x = String::new();
+        f(&x);
+        f2(&x);
+    }
+    // https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280
+    // issue 9111
+    {
+        struct A;
+
+        impl Extend<u8> for A {
+            fn extend<T: IntoIterator<Item = u8>>(&mut self, _: T) {
+                unimplemented!()
+            }
+        }
+
+        impl<'a> Extend<&'a u8> for A {
+            fn extend<T: IntoIterator<Item = &'a u8>>(&mut self, _: T) {
+                unimplemented!()
+            }
+        }
+
+        let mut a = A;
+        a.extend(&[]); // vs a.extend([]);
+    }
+    // issue 9710
+    {
+        fn f(_: impl AsRef<str>) {}
+
+        let x = String::new();
+        for _ in 0..10 {
+            f(&x);
+        }
+    }
+    // issue 9739
+    {
+        fn foo<D: Display>(_it: impl IntoIterator<Item = D>) {}
+        foo(if std::env::var_os("HI").is_some() {
+            &[0]
+        } else {
+            &[] as &[u32]
+        });
+    }
+    {
+        struct S;
+
+        impl S {
+            fn foo<D: Display>(&self, _it: impl IntoIterator<Item = D>) {}
+        }
+
+        S.foo(if std::env::var_os("HI").is_some() {
+            &[0]
+        } else {
+            &[] as &[u32]
+        });
+    }
+    // issue 9782
+    {
+        fn foo<T: AsRef<[u8]>>(t: T) {
+            println!("{}", std::mem::size_of::<T>());
+            let _t: &[u8] = t.as_ref();
+        }
+
+        let a: [u8; 100] = [0u8; 100];
+
+        // 100
+        foo::<[u8; 100]>(a);
+        foo(a);
+
+        // 16
+        foo::<&[u8]>(&a);
+        foo(a.as_slice());
+
+        // 8
+        foo::<&[u8; 100]>(&a);
+        foo(a);
+    }
+    {
+        struct S;
+
+        impl S {
+            fn foo<T: AsRef<[u8]>>(t: T) {
+                println!("{}", std::mem::size_of::<T>());
+                let _t: &[u8] = t.as_ref();
+            }
+        }
+
+        let a: [u8; 100] = [0u8; 100];
+        S::foo::<&[u8; 100]>(&a);
+    }
+    {
+        struct S;
+
+        impl S {
+            fn foo<T: AsRef<[u8]>>(&self, t: T) {
+                println!("{}", std::mem::size_of::<T>());
+                let _t: &[u8] = t.as_ref();
+            }
+        }
+
+        let a: [u8; 100] = [0u8; 100];
+        S.foo::<&[u8; 100]>(&a);
+    }
+    // issue 10535
+    {
+        static SOME_STATIC: String = String::new();
+
+        static UNIT: () = compute(&SOME_STATIC);
+
+        pub const fn compute<T>(_: T)
+        where
+            T: Copy,
+        {
+        }
+    }
+}
diff --git a/tests/ui/needless_borrows_for_generic_args.rs b/tests/ui/needless_borrows_for_generic_args.rs
new file mode 100644
index 00000000000..f0567f486ac
--- /dev/null
+++ b/tests/ui/needless_borrows_for_generic_args.rs
@@ -0,0 +1,287 @@
+#![warn(clippy::needless_borrows_for_generic_args)]
+#![allow(
+    clippy::unnecessary_to_owned,
+    clippy::unnecessary_literal_unwrap,
+    clippy::needless_borrow
+)]
+
+use core::ops::Deref;
+use std::any::Any;
+use std::ffi::OsStr;
+use std::fmt::{Debug, Display};
+use std::path::Path;
+use std::process::Command;
+
+fn main() {
+    let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap();
+    let _ = Path::new(".").join(&&".");
+    let _ = Any::type_id(&""); // Don't lint. `Any` is only bound
+    let _ = Box::new(&""); // Don't lint. Type parameter appears in return type
+    let _ = Some("").unwrap_or(&"");
+    let _ = std::fs::write("x", &"".to_string());
+
+    {
+        #[derive(Clone, Copy)]
+        struct X;
+
+        impl Deref for X {
+            type Target = X;
+            fn deref(&self) -> &Self::Target {
+                self
+            }
+        }
+
+        fn deref_target_is_x<T: Deref<Target = X>>(_: T) {}
+
+        deref_target_is_x(&X);
+    }
+    {
+        fn multiple_constraints<T, U, V, X, Y>(_: T)
+        where
+            T: IntoIterator<Item = U> + IntoIterator<Item = X>,
+            U: IntoIterator<Item = V>,
+            V: AsRef<str>,
+            X: IntoIterator<Item = Y>,
+            Y: AsRef<OsStr>,
+        {
+        }
+
+        multiple_constraints(&[[""]]);
+    }
+    {
+        #[derive(Clone, Copy)]
+        struct X;
+
+        impl Deref for X {
+            type Target = X;
+            fn deref(&self) -> &Self::Target {
+                self
+            }
+        }
+
+        fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V)
+        where
+            T: Deref<Target = U>,
+            U: Deref<Target = V>,
+        {
+        }
+
+        multiple_constraints_normalizes_to_same(&X, X);
+    }
+    {
+        fn only_sized<T>(_: T) {}
+        only_sized(&""); // Don't lint. `Sized` is only bound
+    }
+    {
+        fn ref_as_ref_path<T: 'static>(_: &'static T)
+        where
+            &'static T: AsRef<Path>,
+        {
+        }
+
+        ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter
+    }
+    {
+        trait RefsOnly {
+            type Referent;
+        }
+
+        impl<T> RefsOnly for &T {
+            type Referent = T;
+        }
+
+        fn refs_only<T, U>(_: T)
+        where
+            T: RefsOnly<Referent = U>,
+        {
+        }
+
+        refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't
+    }
+    {
+        fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U)
+        where
+            T: IntoIterator<Item = U>,
+            U: IntoIterator<Item = V>,
+            V: AsRef<str>,
+        {
+        }
+        multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments
+    }
+    // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
+    {
+        #[derive(Clone, Copy)]
+        struct Iter;
+        impl Iterator for Iter {
+            type Item = ();
+            fn next(&mut self) -> Option<Self::Item> {
+                None
+            }
+        }
+        fn takes_iter(_: impl Iterator) {}
+        fn dont_warn(mut x: Iter) {
+            takes_iter(&mut x);
+        }
+        #[allow(unused_mut)]
+        fn warn(mut x: &mut Iter) {
+            takes_iter(&mut x)
+        }
+    }
+    #[clippy::msrv = "1.52.0"]
+    {
+        let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap();
+    };
+    #[clippy::msrv = "1.53.0"]
+    {
+        let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap();
+    };
+    {
+        let env = "env".to_owned();
+        let arg = "arg".to_owned();
+        let f = |arg| {
+            let loc = "loc".to_owned();
+            let _ = std::fs::write("x", &env); // Don't lint. In environment
+            let _ = std::fs::write("x", &arg);
+            let _ = std::fs::write("x", &loc);
+        };
+        let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f`
+        f(arg);
+    }
+    {
+        #[derive(Debug)]
+        struct X;
+
+        impl Drop for X {
+            fn drop(&mut self) {}
+        }
+
+        fn f(_: impl Debug) {}
+
+        let x = X;
+        f(&x); // Don't lint. Has significant drop
+    }
+    {
+        fn f(_: impl AsRef<str>) {}
+
+        let x = String::new();
+        f(&x);
+    }
+    {
+        fn f(_: impl AsRef<str>) {}
+        fn f2(_: impl AsRef<str>) {}
+
+        let x = String::new();
+        f(&x);
+        f2(&x);
+    }
+    // https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280
+    // issue 9111
+    {
+        struct A;
+
+        impl Extend<u8> for A {
+            fn extend<T: IntoIterator<Item = u8>>(&mut self, _: T) {
+                unimplemented!()
+            }
+        }
+
+        impl<'a> Extend<&'a u8> for A {
+            fn extend<T: IntoIterator<Item = &'a u8>>(&mut self, _: T) {
+                unimplemented!()
+            }
+        }
+
+        let mut a = A;
+        a.extend(&[]); // vs a.extend([]);
+    }
+    // issue 9710
+    {
+        fn f(_: impl AsRef<str>) {}
+
+        let x = String::new();
+        for _ in 0..10 {
+            f(&x);
+        }
+    }
+    // issue 9739
+    {
+        fn foo<D: Display>(_it: impl IntoIterator<Item = D>) {}
+        foo(if std::env::var_os("HI").is_some() {
+            &[0]
+        } else {
+            &[] as &[u32]
+        });
+    }
+    {
+        struct S;
+
+        impl S {
+            fn foo<D: Display>(&self, _it: impl IntoIterator<Item = D>) {}
+        }
+
+        S.foo(if std::env::var_os("HI").is_some() {
+            &[0]
+        } else {
+            &[] as &[u32]
+        });
+    }
+    // issue 9782
+    {
+        fn foo<T: AsRef<[u8]>>(t: T) {
+            println!("{}", std::mem::size_of::<T>());
+            let _t: &[u8] = t.as_ref();
+        }
+
+        let a: [u8; 100] = [0u8; 100];
+
+        // 100
+        foo::<[u8; 100]>(a);
+        foo(a);
+
+        // 16
+        foo::<&[u8]>(&a);
+        foo(a.as_slice());
+
+        // 8
+        foo::<&[u8; 100]>(&a);
+        foo(&a);
+    }
+    {
+        struct S;
+
+        impl S {
+            fn foo<T: AsRef<[u8]>>(t: T) {
+                println!("{}", std::mem::size_of::<T>());
+                let _t: &[u8] = t.as_ref();
+            }
+        }
+
+        let a: [u8; 100] = [0u8; 100];
+        S::foo::<&[u8; 100]>(&a);
+    }
+    {
+        struct S;
+
+        impl S {
+            fn foo<T: AsRef<[u8]>>(&self, t: T) {
+                println!("{}", std::mem::size_of::<T>());
+                let _t: &[u8] = t.as_ref();
+            }
+        }
+
+        let a: [u8; 100] = [0u8; 100];
+        S.foo::<&[u8; 100]>(&a);
+    }
+    // issue 10535
+    {
+        static SOME_STATIC: String = String::new();
+
+        static UNIT: () = compute(&SOME_STATIC);
+
+        pub const fn compute<T>(_: T)
+        where
+            T: Copy,
+        {
+        }
+    }
+}
diff --git a/tests/ui/needless_borrows_for_generic_args.stderr b/tests/ui/needless_borrows_for_generic_args.stderr
new file mode 100644
index 00000000000..e2cde2c59a6
--- /dev/null
+++ b/tests/ui/needless_borrows_for_generic_args.stderr
@@ -0,0 +1,77 @@
+error: the borrowed expression implements the required traits
+  --> $DIR/needless_borrows_for_generic_args.rs:16:37
+   |
+LL |     let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap();
+   |                                     ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
+   |
+   = note: `-D clippy::needless-borrows-for-generic-args` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::needless_borrows_for_generic_args)]`
+
+error: the borrowed expression implements the required traits
+  --> $DIR/needless_borrows_for_generic_args.rs:17:33
+   |
+LL |     let _ = Path::new(".").join(&&".");
+   |                                 ^^^^^ help: change this to: `"."`
+
+error: the borrowed expression implements the required traits
+  --> $DIR/needless_borrows_for_generic_args.rs:21:33
+   |
+LL |     let _ = std::fs::write("x", &"".to_string());
+   |                                 ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()`
+
+error: the borrowed expression implements the required traits
+  --> $DIR/needless_borrows_for_generic_args.rs:36:27
+   |
+LL |         deref_target_is_x(&X);
+   |                           ^^ help: change this to: `X`
+
+error: the borrowed expression implements the required traits
+  --> $DIR/needless_borrows_for_generic_args.rs:49:30
+   |
+LL |         multiple_constraints(&[[""]]);
+   |                              ^^^^^^^ help: change this to: `[[""]]`
+
+error: the borrowed expression implements the required traits
+  --> $DIR/needless_borrows_for_generic_args.rs:69:49
+   |
+LL |         multiple_constraints_normalizes_to_same(&X, X);
+   |                                                 ^^ help: change this to: `X`
+
+error: the borrowed expression implements the required traits
+  --> $DIR/needless_borrows_for_generic_args.rs:127:24
+   |
+LL |             takes_iter(&mut x)
+   |                        ^^^^^^ help: change this to: `x`
+
+error: the borrowed expression implements the required traits
+  --> $DIR/needless_borrows_for_generic_args.rs:136:41
+   |
+LL |         let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap();
+   |                                         ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
+
+error: the borrowed expression implements the required traits
+  --> $DIR/needless_borrows_for_generic_args.rs:144:41
+   |
+LL |             let _ = std::fs::write("x", &arg);
+   |                                         ^^^^ help: change this to: `arg`
+
+error: the borrowed expression implements the required traits
+  --> $DIR/needless_borrows_for_generic_args.rs:145:41
+   |
+LL |             let _ = std::fs::write("x", &loc);
+   |                                         ^^^^ help: change this to: `loc`
+
+error: the borrowed expression implements the required traits
+  --> $DIR/needless_borrows_for_generic_args.rs:167:11
+   |
+LL |         f(&x);
+   |           ^^ help: change this to: `x`
+
+error: the borrowed expression implements the required traits
+  --> $DIR/needless_borrows_for_generic_args.rs:247:13
+   |
+LL |         foo(&a);
+   |             ^^ help: change this to: `a`
+
+error: aborting due to 12 previous errors
+
diff --git a/tests/ui/regex.rs b/tests/ui/regex.rs
index 5259d9ce04b..094d9574ae9 100644
--- a/tests/ui/regex.rs
+++ b/tests/ui/regex.rs
@@ -2,7 +2,8 @@
     unused,
     clippy::needless_raw_strings,
     clippy::needless_raw_string_hashes,
-    clippy::needless_borrow
+    clippy::needless_borrow,
+    clippy::needless_borrows_for_generic_args
 )]
 #![warn(clippy::invalid_regex, clippy::trivial_regex)]
 
diff --git a/tests/ui/regex.stderr b/tests/ui/regex.stderr
index 91f90157e68..6d98d691d6f 100644
--- a/tests/ui/regex.stderr
+++ b/tests/ui/regex.stderr
@@ -1,5 +1,5 @@
 error: trivial regex
-  --> $DIR/regex.rs:18:45
+  --> $DIR/regex.rs:19:45
    |
 LL |     let pipe_in_wrong_position = Regex::new("|");
    |                                             ^^^
@@ -9,7 +9,7 @@ LL |     let pipe_in_wrong_position = Regex::new("|");
    = help: to override `-D warnings` add `#[allow(clippy::trivial_regex)]`
 
 error: trivial regex
-  --> $DIR/regex.rs:20:60
+  --> $DIR/regex.rs:21:60
    |
 LL |     let pipe_in_wrong_position_builder = RegexBuilder::new("|");
    |                                                            ^^^
@@ -17,7 +17,7 @@ LL |     let pipe_in_wrong_position_builder = RegexBuilder::new("|");
    = help: the regex is unlikely to be useful as it is
 
 error: regex syntax error: invalid character class range, the start must be <= the end
-  --> $DIR/regex.rs:22:42
+  --> $DIR/regex.rs:23:42
    |
 LL |     let wrong_char_ranice = Regex::new("[z-a]");
    |                                          ^^^
@@ -26,7 +26,7 @@ LL |     let wrong_char_ranice = Regex::new("[z-a]");
    = help: to override `-D warnings` add `#[allow(clippy::invalid_regex)]`
 
 error: regex syntax error: invalid character class range, the start must be <= the end
-  --> $DIR/regex.rs:25:37
+  --> $DIR/regex.rs:26:37
    |
 LL |     let some_unicode = Regex::new("[é-è]");
    |                                     ^^^
@@ -35,13 +35,13 @@ error: regex parse error:
            (
            ^
        error: unclosed group
-  --> $DIR/regex.rs:28:33
+  --> $DIR/regex.rs:29:33
    |
 LL |     let some_regex = Regex::new(OPENING_PAREN);
    |                                 ^^^^^^^^^^^^^
 
 error: trivial regex
-  --> $DIR/regex.rs:30:53
+  --> $DIR/regex.rs:31:53
    |
 LL |     let binary_pipe_in_wrong_position = BRegex::new("|");
    |                                                     ^^^
@@ -52,7 +52,7 @@ error: regex parse error:
            (
            ^
        error: unclosed group
-  --> $DIR/regex.rs:32:41
+  --> $DIR/regex.rs:33:41
    |
 LL |     let some_binary_regex = BRegex::new(OPENING_PAREN);
    |                                         ^^^^^^^^^^^^^
@@ -61,7 +61,7 @@ error: regex parse error:
            (
            ^
        error: unclosed group
-  --> $DIR/regex.rs:33:56
+  --> $DIR/regex.rs:34:56
    |
 LL |     let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN);
    |                                                        ^^^^^^^^^^^^^
@@ -70,7 +70,7 @@ error: regex parse error:
            (
            ^
        error: unclosed group
-  --> $DIR/regex.rs:45:37
+  --> $DIR/regex.rs:46:37
    |
 LL |     let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]);
    |                                     ^^^^^^^^^^^^^
@@ -79,7 +79,7 @@ error: regex parse error:
            (
            ^
        error: unclosed group
-  --> $DIR/regex.rs:46:39
+  --> $DIR/regex.rs:47:39
    |
 LL |     let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]);
    |                                       ^^^^^^^^^^^^^
@@ -88,7 +88,7 @@ error: regex parse error:
            \b\c
              ^^
        error: unrecognized escape sequence
-  --> $DIR/regex.rs:53:42
+  --> $DIR/regex.rs:54:42
    |
 LL |     let escaped_string_span = Regex::new("\\b\\c");
    |                                          ^^^^^^^^
@@ -96,19 +96,19 @@ LL |     let escaped_string_span = Regex::new("\\b\\c");
    = help: consider using a raw string literal: `r".."`
 
 error: regex syntax error: duplicate flag
-  --> $DIR/regex.rs:55:34
+  --> $DIR/regex.rs:56:34
    |
 LL |     let aux_span = Regex::new("(?ixi)");
    |                                  ^ ^
 
 error: regex syntax error: pattern can match invalid UTF-8
-  --> $DIR/regex.rs:61:53
+  --> $DIR/regex.rs:62:53
    |
 LL |     let invalid_utf8_should_lint = Regex::new("(?-u).");
    |                                                     ^
 
 error: trivial regex
-  --> $DIR/regex.rs:66:33
+  --> $DIR/regex.rs:67:33
    |
 LL |     let trivial_eq = Regex::new("^foobar$");
    |                                 ^^^^^^^^^^
@@ -116,7 +116,7 @@ LL |     let trivial_eq = Regex::new("^foobar$");
    = help: consider using `==` on `str`s
 
 error: trivial regex
-  --> $DIR/regex.rs:69:48
+  --> $DIR/regex.rs:70:48
    |
 LL |     let trivial_eq_builder = RegexBuilder::new("^foobar$");
    |                                                ^^^^^^^^^^
@@ -124,7 +124,7 @@ LL |     let trivial_eq_builder = RegexBuilder::new("^foobar$");
    = help: consider using `==` on `str`s
 
 error: trivial regex
-  --> $DIR/regex.rs:72:42
+  --> $DIR/regex.rs:73:42
    |
 LL |     let trivial_starts_with = Regex::new("^foobar");
    |                                          ^^^^^^^^^
@@ -132,7 +132,7 @@ LL |     let trivial_starts_with = Regex::new("^foobar");
    = help: consider using `str::starts_with`
 
 error: trivial regex
-  --> $DIR/regex.rs:75:40
+  --> $DIR/regex.rs:76:40
    |
 LL |     let trivial_ends_with = Regex::new("foobar$");
    |                                        ^^^^^^^^^
@@ -140,7 +140,7 @@ LL |     let trivial_ends_with = Regex::new("foobar$");
    = help: consider using `str::ends_with`
 
 error: trivial regex
-  --> $DIR/regex.rs:78:39
+  --> $DIR/regex.rs:79:39
    |
 LL |     let trivial_contains = Regex::new("foobar");
    |                                       ^^^^^^^^
@@ -148,7 +148,7 @@ LL |     let trivial_contains = Regex::new("foobar");
    = help: consider using `str::contains`
 
 error: trivial regex
-  --> $DIR/regex.rs:81:39
+  --> $DIR/regex.rs:82:39
    |
 LL |     let trivial_contains = Regex::new(NOT_A_REAL_REGEX);
    |                                       ^^^^^^^^^^^^^^^^
@@ -156,7 +156,7 @@ LL |     let trivial_contains = Regex::new(NOT_A_REAL_REGEX);
    = help: consider using `str::contains`
 
 error: trivial regex
-  --> $DIR/regex.rs:84:40
+  --> $DIR/regex.rs:85:40
    |
 LL |     let trivial_backslash = Regex::new("a\\.b");
    |                                        ^^^^^^^
@@ -164,7 +164,7 @@ LL |     let trivial_backslash = Regex::new("a\\.b");
    = help: consider using `str::contains`
 
 error: trivial regex
-  --> $DIR/regex.rs:88:36
+  --> $DIR/regex.rs:89:36
    |
 LL |     let trivial_empty = Regex::new("");
    |                                    ^^
@@ -172,7 +172,7 @@ LL |     let trivial_empty = Regex::new("");
    = help: the regex is unlikely to be useful as it is
 
 error: trivial regex
-  --> $DIR/regex.rs:91:36
+  --> $DIR/regex.rs:92:36
    |
 LL |     let trivial_empty = Regex::new("^");
    |                                    ^^^
@@ -180,7 +180,7 @@ LL |     let trivial_empty = Regex::new("^");
    = help: the regex is unlikely to be useful as it is
 
 error: trivial regex
-  --> $DIR/regex.rs:94:36
+  --> $DIR/regex.rs:95:36
    |
 LL |     let trivial_empty = Regex::new("^$");
    |                                    ^^^^
@@ -188,7 +188,7 @@ LL |     let trivial_empty = Regex::new("^$");
    = help: consider using `str::is_empty`
 
 error: trivial regex
-  --> $DIR/regex.rs:97:44
+  --> $DIR/regex.rs:98:44
    |
 LL |     let binary_trivial_empty = BRegex::new("^$");
    |                                            ^^^^
diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed
index 7b662ca92d2..67faabc53cb 100644
--- a/tests/ui/unnecessary_to_owned.fixed
+++ b/tests/ui/unnecessary_to_owned.fixed
@@ -1,4 +1,4 @@
-#![allow(clippy::needless_borrow, clippy::ptr_arg)]
+#![allow(clippy::needless_borrow, clippy::needless_borrows_for_generic_args, clippy::ptr_arg)]
 #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)]
 
 use std::borrow::Cow;
diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs
index d79778a6a2e..99f9136427d 100644
--- a/tests/ui/unnecessary_to_owned.rs
+++ b/tests/ui/unnecessary_to_owned.rs
@@ -1,4 +1,4 @@
-#![allow(clippy::needless_borrow, clippy::ptr_arg)]
+#![allow(clippy::needless_borrow, clippy::needless_borrows_for_generic_args, clippy::ptr_arg)]
 #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)]
 
 use std::borrow::Cow;