about summary refs log tree commit diff
path: root/clippy_lints/src/methods
diff options
context:
space:
mode:
authorflip1995 <philipp.krones@embecosm.com>2021-12-17 13:40:22 +0100
committerflip1995 <philipp.krones@embecosm.com>2021-12-17 13:40:22 +0100
commitece0946d7f60bdee8b9544753a3f1a3acddcb1f7 (patch)
tree78cfdf1e08b149bae2122b496dd4cfd6fb55c4d4 /clippy_lints/src/methods
parentb2f8a27ff2662b440d0dd65cdb609d303b2fa280 (diff)
Merge commit '23d11428de3e973b34a5090a78d62887f821c90e' into clippyup
Diffstat (limited to 'clippy_lints/src/methods')
-rw-r--r--clippy_lints/src/methods/bind_instead_of_map.rs4
-rw-r--r--clippy_lints/src/methods/filter_map.rs4
-rw-r--r--clippy_lints/src/methods/implicit_clone.rs29
-rw-r--r--clippy_lints/src/methods/mod.rs32
-rw-r--r--clippy_lints/src/methods/option_as_ref_deref.rs4
-rw-r--r--clippy_lints/src/methods/unnecessary_fold.rs4
-rw-r--r--clippy_lints/src/methods/unnecessary_iter_cloned.rs177
-rw-r--r--clippy_lints/src/methods/unnecessary_to_owned.rs397
8 files changed, 633 insertions, 18 deletions
diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs
index b7690cf9222..150bafc0f5d 100644
--- a/clippy_lints/src/methods/bind_instead_of_map.rs
+++ b/clippy_lints/src/methods/bind_instead_of_map.rs
@@ -1,7 +1,7 @@
 use super::{contains_return, BIND_INSTEAD_OF_MAP};
 use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::{snippet, snippet_with_macro_callsite};
-use clippy_utils::{remove_blocks, visitors::find_all_ret_expressions};
+use clippy_utils::{peel_blocks, visitors::find_all_ret_expressions};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -152,7 +152,7 @@ pub(crate) trait BindInsteadOfMap {
         match arg.kind {
             hir::ExprKind::Closure(_, _, body_id, closure_args_span, _) => {
                 let closure_body = cx.tcx.hir().body(body_id);
-                let closure_expr = remove_blocks(&closure_body.value);
+                let closure_expr = peel_blocks(&closure_body.value);
 
                 if Self::lint_closure_autofixable(cx, expr, recv, closure_expr, closure_args_span) {
                     true
diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs
index c96c817bb8b..6d8733c08b4 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;
 use clippy_utils::source::{indent_of, reindent_multiline, snippet};
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_trait_method, path_to_local_id, remove_blocks, SpanlessEq};
+use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, SpanlessEq};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -25,7 +25,7 @@ fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Sy
         },
         hir::ExprKind::Closure(_, _, c, _, _) => {
             let body = cx.tcx.hir().body(*c);
-            let closure_expr = remove_blocks(&body.value);
+            let closure_expr = peel_blocks(&body.value);
             let arg_id = body.params[0].pat.hir_id;
             match closure_expr.kind {
                 hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, _, args, _) => {
diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs
index 81c42de145f..90492ffda3c 100644
--- a/clippy_lints/src/methods/implicit_clone.rs
+++ b/clippy_lints/src/methods/implicit_clone.rs
@@ -12,15 +12,7 @@ use super::IMPLICIT_CLONE;
 pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, span: Span) {
     if_chain! {
         if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
-        if match method_name {
-            "to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr),
-            "to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned),
-            "to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path),
-            "to_vec" => cx.tcx.impl_of_method(method_def_id)
-                .map(|impl_did| Some(impl_did) == cx.tcx.lang_items().slice_alloc_impl())
-                == Some(true),
-            _ => false,
-        };
+        if is_clone_like(cx, method_name, method_def_id);
         let return_type = cx.typeck_results().expr_ty(expr);
         let input_type = cx.typeck_results().expr_ty(recv).peel_refs();
         if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did));
@@ -38,3 +30,22 @@ pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv
         }
     }
 }
+
+/// Returns true if the named method can be used to clone the receiver.
+/// Note that `to_string` is not flagged by `implicit_clone`. So other lints that call
+/// `is_clone_like` and that do flag `to_string` must handle it separately. See, e.g.,
+/// `is_to_owned_like` in `unnecessary_to_owned.rs`.
+pub fn is_clone_like(cx: &LateContext<'_>, method_name: &str, method_def_id: hir::def_id::DefId) -> bool {
+    match method_name {
+        "to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr),
+        "to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned),
+        "to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path),
+        "to_vec" => {
+            cx.tcx
+                .impl_of_method(method_def_id)
+                .map(|impl_did| Some(impl_did) == cx.tcx.lang_items().slice_alloc_impl())
+                == Some(true)
+        },
+        _ => false,
+    }
+}
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 58ec2213535..3c43671dd34 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -56,7 +56,9 @@ mod suspicious_splitn;
 mod uninit_assumed_init;
 mod unnecessary_filter_map;
 mod unnecessary_fold;
+mod unnecessary_iter_cloned;
 mod unnecessary_lazy_eval;
+mod unnecessary_to_owned;
 mod unwrap_or_else_default;
 mod unwrap_used;
 mod useless_asref;
@@ -1885,6 +1887,32 @@ declare_clippy_lint! {
     "usages of `str::splitn` that can be replaced with `str::split`"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for unnecessary calls to [`ToOwned::to_owned`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#tymethod.to_owned)
+    /// and other `to_owned`-like functions.
+    ///
+    /// ### Why is this bad?
+    /// The unnecessary calls result in useless allocations.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let path = std::path::Path::new("x");
+    /// foo(&path.to_string_lossy().to_string());
+    /// fn foo(s: &str) {}
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let path = std::path::Path::new("x");
+    /// foo(&path.to_string_lossy());
+    /// fn foo(s: &str) {}
+    /// ```
+    #[clippy::version = "1.58.0"]
+    pub UNNECESSARY_TO_OWNED,
+    perf,
+    "unnecessary calls to `to_owned`-like functions"
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Option<RustcVersion>,
@@ -1964,7 +1992,8 @@ impl_lint_pass!(Methods => [
     MANUAL_STR_REPEAT,
     EXTEND_WITH_DRAIN,
     MANUAL_SPLIT_ONCE,
-    NEEDLESS_SPLITN
+    NEEDLESS_SPLITN,
+    UNNECESSARY_TO_OWNED,
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -2007,6 +2036,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
                 single_char_add_str::check(cx, expr, args);
                 into_iter_on_ref::check(cx, expr, *method_span, method_call.ident.name, args);
                 single_char_pattern::check(cx, expr, method_call.ident.name, args);
+                unnecessary_to_owned::check(cx, expr, method_call.ident.name, args);
             },
             hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
                 let mut info = BinaryExprInfo {
diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs
index d3f40d26208..fa74a8f3dc3 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::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, remove_blocks};
+use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, peel_blocks};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -53,7 +53,7 @@ pub(super) fn check<'tcx>(
             }),
         hir::ExprKind::Closure(_, _, body_id, _, _) => {
             let closure_body = cx.tcx.hir().body(body_id);
-            let closure_expr = remove_blocks(&closure_body.value);
+            let closure_expr = peel_blocks(&closure_body.value);
 
             match &closure_expr.kind {
                 hir::ExprKind::MethodCall(_, _, args, _) => {
diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs
index 4c4034437da..47a81199608 100644
--- a/clippy_lints/src/methods/unnecessary_fold.rs
+++ b/clippy_lints/src/methods/unnecessary_fold.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{is_trait_method, path_to_local_id, remove_blocks, strip_pat_refs};
+use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, strip_pat_refs};
 use if_chain::if_chain;
 use rustc_ast::ast;
 use rustc_errors::Applicability;
@@ -31,7 +31,7 @@ pub(super) fn check(
             // Extract the body of the closure passed to fold
             if let hir::ExprKind::Closure(_, _, body_id, _, _) = acc.kind;
             let closure_body = cx.tcx.hir().body(body_id);
-            let closure_expr = remove_blocks(&closure_body.value);
+            let closure_expr = peel_blocks(&closure_body.value);
 
             // Check if the closure body is of the form `acc <op> some_expr(x)`
             if let hir::ExprKind::Binary(ref bin_op, left_expr, right_expr) = closure_expr.kind;
diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs
new file mode 100644
index 00000000000..8300df03e99
--- /dev/null
+++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs
@@ -0,0 +1,177 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::higher::ForLoop;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait};
+use clippy_utils::{fn_def_id, get_parent_expr, path_to_local_id, usage};
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, HirId, LangItem, Mutability, Pat};
+use rustc_lint::LateContext;
+use rustc_middle::{hir::map::Map, ty};
+use rustc_span::{sym, Symbol};
+
+use super::UNNECESSARY_TO_OWNED;
+
+pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, receiver: &'tcx Expr<'tcx>) -> bool {
+    if_chain! {
+        if let Some(parent) = get_parent_expr(cx, expr);
+        if let Some(callee_def_id) = fn_def_id(cx, parent);
+        if is_into_iter(cx, callee_def_id);
+        then {
+            check_for_loop_iter(cx, parent, method_name, receiver)
+        } else {
+            false
+        }
+    }
+}
+
+/// Checks whether `expr` is an iterator in a `for` loop and, if so, determines whether the
+/// iterated-over items could be iterated over by reference. The reason why `check` above does not
+/// include this code directly is so that it can be called from
+/// `unnecessary_into_owned::check_into_iter_call_arg`.
+pub fn check_for_loop_iter(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    method_name: Symbol,
+    receiver: &'tcx Expr<'tcx>,
+) -> bool {
+    if_chain! {
+        if let Some(grandparent) = get_parent_expr(cx, expr).and_then(|parent| get_parent_expr(cx, parent));
+        if let Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent);
+        let (clone_or_copy_needed, addr_of_exprs) = clone_or_copy_needed(cx, pat, body);
+        if !clone_or_copy_needed;
+        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
+        then {
+            let snippet = if_chain! {
+                if let ExprKind::MethodCall(maybe_iter_method_name, _, [collection], _) = receiver.kind;
+                if maybe_iter_method_name.ident.name == sym::iter;
+
+                if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
+                let receiver_ty = cx.typeck_results().expr_ty(receiver);
+                if implements_trait(cx, receiver_ty, iterator_trait_id, &[]);
+                if let Some(iter_item_ty) = get_iterator_item_ty(cx, receiver_ty);
+
+                if let Some(into_iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator);
+                let collection_ty = cx.typeck_results().expr_ty(collection);
+                if implements_trait(cx, collection_ty, into_iterator_trait_id, &[]);
+                if let Some(into_iter_item_ty) = get_associated_type(cx, collection_ty, into_iterator_trait_id, "Item");
+
+                if iter_item_ty == into_iter_item_ty;
+                if let Some(collection_snippet) = snippet_opt(cx, collection.span);
+                then {
+                    collection_snippet
+                } else {
+                    receiver_snippet
+                }
+            };
+            span_lint_and_then(
+                cx,
+                UNNECESSARY_TO_OWNED,
+                expr.span,
+                &format!("unnecessary use of `{}`", method_name),
+                |diag| {
+                    diag.span_suggestion(expr.span, "use", snippet, Applicability::MachineApplicable);
+                    for addr_of_expr in addr_of_exprs {
+                        match addr_of_expr.kind {
+                            ExprKind::AddrOf(_, _, referent) => {
+                                let span = addr_of_expr.span.with_hi(referent.span.lo());
+                                diag.span_suggestion(span, "remove this `&`", String::new(), Applicability::MachineApplicable);
+                            }
+                            _ => unreachable!(),
+                        }
+                    }
+                }
+            );
+            return true;
+        }
+    }
+    false
+}
+
+/// The core logic of `check_for_loop_iter` above, this function wraps a use of
+/// `CloneOrCopyVisitor`.
+fn clone_or_copy_needed(
+    cx: &LateContext<'tcx>,
+    pat: &Pat<'tcx>,
+    body: &'tcx Expr<'tcx>,
+) -> (bool, Vec<&'tcx Expr<'tcx>>) {
+    let mut visitor = CloneOrCopyVisitor {
+        cx,
+        binding_hir_ids: pat_bindings(pat),
+        clone_or_copy_needed: false,
+        addr_of_exprs: Vec::new(),
+    };
+    visitor.visit_expr(body);
+    (visitor.clone_or_copy_needed, visitor.addr_of_exprs)
+}
+
+/// Returns a vector of all `HirId`s bound by the pattern.
+fn pat_bindings(pat: &Pat<'_>) -> Vec<HirId> {
+    let mut collector = usage::ParamBindingIdCollector {
+        binding_hir_ids: Vec::new(),
+    };
+    collector.visit_pat(pat);
+    collector.binding_hir_ids
+}
+
+/// `clone_or_copy_needed` will be false when `CloneOrCopyVisitor` is done visiting if the only
+/// operations performed on `binding_hir_ids` are:
+/// * to take non-mutable references to them
+/// * to use them as non-mutable `&self` in method calls
+/// If any of `binding_hir_ids` is used in any other way, then `clone_or_copy_needed` will be true
+/// when `CloneOrCopyVisitor` is done visiting.
+struct CloneOrCopyVisitor<'cx, 'tcx> {
+    cx: &'cx LateContext<'tcx>,
+    binding_hir_ids: Vec<HirId>,
+    clone_or_copy_needed: bool,
+    addr_of_exprs: Vec<&'tcx Expr<'tcx>>,
+}
+
+impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> {
+    type Map = Map<'tcx>;
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+    }
+
+    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+        walk_expr(self, expr);
+        if self.is_binding(expr) {
+            if let Some(parent) = get_parent_expr(self.cx, expr) {
+                match parent.kind {
+                    ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) => {
+                        self.addr_of_exprs.push(parent);
+                        return;
+                    },
+                    ExprKind::MethodCall(_, _, args, _) => {
+                        if_chain! {
+                            if args.iter().skip(1).all(|arg| !self.is_binding(arg));
+                            if let Some(method_def_id) = self.cx.typeck_results().type_dependent_def_id(parent.hir_id);
+                            let method_ty = self.cx.tcx.type_of(method_def_id);
+                            let self_ty = method_ty.fn_sig(self.cx.tcx).input(0).skip_binder();
+                            if matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Not));
+                            then {
+                                return;
+                            }
+                        }
+                    },
+                    _ => {},
+                }
+            }
+            self.clone_or_copy_needed = true;
+        }
+    }
+}
+
+impl<'cx, 'tcx> CloneOrCopyVisitor<'cx, 'tcx> {
+    fn is_binding(&self, expr: &Expr<'tcx>) -> bool {
+        self.binding_hir_ids
+            .iter()
+            .any(|hir_id| path_to_local_id(expr, *hir_id))
+    }
+}
+
+/// Returns true if the named method is `IntoIterator::into_iter`.
+pub fn is_into_iter(cx: &LateContext<'_>, callee_def_id: DefId) -> bool {
+    cx.tcx.lang_items().require(LangItem::IntoIterIntoIter) == Ok(callee_def_id)
+}
diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs
new file mode 100644
index 00000000000..c48bacfce0d
--- /dev/null
+++ b/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -0,0 +1,397 @@
+use super::implicit_clone::is_clone_like;
+use super::unnecessary_iter_cloned::{self, is_into_iter};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
+use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
+use rustc_errors::Applicability;
+use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::mir::Mutability;
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
+use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
+use rustc_middle::ty::{self, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
+use rustc_span::{sym, Symbol};
+use std::cmp::max;
+
+use super::UNNECESSARY_TO_OWNED;
+
+pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) {
+    if_chain! {
+        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        if let [receiver] = args;
+        then {
+            if is_cloned_or_copied(cx, method_name, method_def_id) {
+                unnecessary_iter_cloned::check(cx, expr, method_name, receiver);
+            } else if is_to_owned_like(cx, method_name, method_def_id) {
+                // At this point, we know the call is of a `to_owned`-like function. The functions
+                // `check_addr_of_expr` and `check_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) {
+                    return;
+                }
+                if check_into_iter_call_arg(cx, expr, method_name, receiver) {
+                    return;
+                }
+                check_other_call_arg(cx, expr, method_name, receiver);
+            }
+        }
+    }
+}
+
+/// 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)]
+fn check_addr_of_expr(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    method_name: Symbol,
+    method_def_id: DefId,
+    receiver: &'tcx Expr<'tcx>,
+) -> bool {
+    if_chain! {
+        if let Some(parent) = get_parent_expr(cx, expr);
+        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind;
+        let adjustments = cx.typeck_results().expr_adjustments(parent).iter().collect::<Vec<_>>();
+        if let Some(target_ty) = match adjustments[..]
+        {
+            // For matching uses of `Cow::from`
+            [
+                Adjustment {
+                    kind: Adjust::Deref(None),
+                    ..
+                },
+                Adjustment {
+                    kind: Adjust::Borrow(_),
+                    target: target_ty,
+                },
+            ]
+            // For matching uses of arrays
+            | [
+                Adjustment {
+                    kind: Adjust::Deref(None),
+                    ..
+                },
+                Adjustment {
+                    kind: Adjust::Borrow(_),
+                    ..
+                },
+                Adjustment {
+                    kind: Adjust::Pointer(_),
+                    target: target_ty,
+                },
+            ]
+            // For matching everything else
+            | [
+                Adjustment {
+                    kind: Adjust::Deref(None),
+                    ..
+                },
+                Adjustment {
+                    kind: Adjust::Deref(Some(OverloadedDeref { .. })),
+                    ..
+                },
+                Adjustment {
+                    kind: Adjust::Borrow(_),
+                    target: target_ty,
+                },
+            ] => Some(target_ty),
+            _ => None,
+        };
+        let receiver_ty = cx.typeck_results().expr_ty(receiver);
+        // Only flag cases where the receiver is copyable or the method is `Cow::into_owned`. This
+        // restriction is to ensure there is not overlap between `redundant_clone` and this lint.
+        if is_copy(cx, receiver_ty) || is_cow_into_owned(cx, method_name, method_def_id);
+        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
+        then {
+            let (target_ty, n_target_refs) = peel_mid_ty_refs(target_ty);
+            let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty);
+            if receiver_ty == target_ty && n_target_refs >= n_receiver_refs {
+                span_lint_and_sugg(
+                    cx,
+                    UNNECESSARY_TO_OWNED,
+                    parent.span,
+                    &format!("unnecessary use of `{}`", method_name),
+                    "use",
+                    format!("{:&>width$}{}", "", receiver_snippet, width = n_target_refs - n_receiver_refs),
+                    Applicability::MachineApplicable,
+                );
+                return true;
+            }
+            if_chain! {
+                if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
+                if implements_trait(cx, receiver_ty, deref_trait_id, &[]);
+                if get_associated_type(cx, receiver_ty, deref_trait_id, "Target") == Some(target_ty);
+                then {
+                    if n_receiver_refs > 0 {
+                        span_lint_and_sugg(
+                            cx,
+                            UNNECESSARY_TO_OWNED,
+                            parent.span,
+                            &format!("unnecessary use of `{}`", method_name),
+                            "use",
+                            receiver_snippet,
+                            Applicability::MachineApplicable,
+                        );
+                    } else {
+                        span_lint_and_sugg(
+                            cx,
+                            UNNECESSARY_TO_OWNED,
+                            expr.span.with_lo(receiver.span.hi()),
+                            &format!("unnecessary use of `{}`", method_name),
+                            "remove this",
+                            String::new(),
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                    return true;
+                }
+            }
+            if_chain! {
+                if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
+                if implements_trait(cx, receiver_ty, as_ref_trait_id, &[GenericArg::from(target_ty)]);
+                then {
+                    span_lint_and_sugg(
+                        cx,
+                        UNNECESSARY_TO_OWNED,
+                        parent.span,
+                        &format!("unnecessary use of `{}`", method_name),
+                        "use",
+                        format!("{}.as_ref()", receiver_snippet),
+                        Applicability::MachineApplicable,
+                    );
+                    return true;
+                }
+            }
+        }
+    }
+    false
+}
+
+/// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its
+/// call of a `to_owned`-like function is unnecessary.
+fn check_into_iter_call_arg(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    method_name: Symbol,
+    receiver: &'tcx Expr<'tcx>,
+) -> bool {
+    if_chain! {
+        if let Some(parent) = get_parent_expr(cx, expr);
+        if let Some(callee_def_id) = fn_def_id(cx, parent);
+        if is_into_iter(cx, callee_def_id);
+        if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
+        let parent_ty = cx.typeck_results().expr_ty(parent);
+        if implements_trait(cx, parent_ty, iterator_trait_id, &[]);
+        if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty);
+        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
+        then {
+            if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver) {
+                return true;
+            }
+            let cloned_or_copied = if is_copy(cx, item_ty) {
+                "copied"
+            } else {
+                "cloned"
+            };
+            span_lint_and_sugg(
+                cx,
+                UNNECESSARY_TO_OWNED,
+                parent.span,
+                &format!("unnecessary use of `{}`", method_name),
+                "use",
+                format!("{}.iter().{}()", receiver_snippet, cloned_or_copied),
+                Applicability::MachineApplicable,
+            );
+            return true;
+        }
+    }
+    false
+}
+
+/// Checks whether `expr` is an argument in a function call and, if so, determines whether its call
+/// of a `to_owned`-like function is unnecessary.
+fn check_other_call_arg(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    method_name: Symbol,
+    receiver: &'tcx Expr<'tcx>,
+) -> bool {
+    if_chain! {
+        if let Some((maybe_call, maybe_arg)) = skip_addr_of_ancestors(cx, expr);
+        if let Some((callee_def_id, call_substs, call_args)) = get_callee_substs_and_args(cx, maybe_call);
+        let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
+        if let Some(i) = call_args.iter().position(|arg| arg.hir_id == maybe_arg.hir_id);
+        if let Some(input) = fn_sig.inputs().get(i);
+        let (input, n_refs) = peel_mid_ty_refs(input);
+        if let (trait_predicates, projection_predicates) = get_input_traits_and_projections(cx, callee_def_id, input);
+        if let Some(sized_def_id) = cx.tcx.lang_items().sized_trait();
+        if let [trait_predicate] = trait_predicates
+            .iter()
+            .filter(|trait_predicate| trait_predicate.def_id() != sized_def_id)
+            .collect::<Vec<_>>()[..];
+        if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
+        if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
+        let receiver_ty = cx.typeck_results().expr_ty(receiver);
+        // If the callee has type parameters, they could appear in `projection_predicate.ty` or the
+        // types of `trait_predicate.trait_ref.substs`.
+        if if trait_predicate.def_id() == deref_trait_id {
+            if let [projection_predicate] = projection_predicates[..] {
+                let normalized_ty =
+                    cx.tcx.subst_and_normalize_erasing_regions(call_substs, cx.param_env, projection_predicate.ty);
+                implements_trait(cx, receiver_ty, deref_trait_id, &[])
+                    && get_associated_type(cx, receiver_ty, deref_trait_id, "Target") == Some(normalized_ty)
+            } else {
+                false
+            }
+        } else if trait_predicate.def_id() == as_ref_trait_id {
+            let composed_substs = compose_substs(
+                cx,
+                &trait_predicate.trait_ref.substs.iter().skip(1).collect::<Vec<_>>()[..],
+                call_substs
+            );
+            implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
+        } else {
+            false
+        };
+        // We can't add an `&` when the trait is `Deref` because `Target = &T` won't match
+        // `Target = T`.
+        if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id;
+        let n_refs = max(n_refs, if is_copy(cx, receiver_ty) { 0 } else { 1 });
+        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
+        then {
+            span_lint_and_sugg(
+                cx,
+                UNNECESSARY_TO_OWNED,
+                maybe_arg.span,
+                &format!("unnecessary use of `{}`", method_name),
+                "use",
+                format!("{:&>width$}{}", "", receiver_snippet, width = n_refs),
+                Applicability::MachineApplicable,
+            );
+            return true;
+        }
+    }
+    false
+}
+
+/// Walks an expression's ancestors until it finds a non-`AddrOf` expression. Returns the first such
+/// expression found (if any) along with the immediately prior expression.
+fn skip_addr_of_ancestors(
+    cx: &LateContext<'tcx>,
+    mut expr: &'tcx Expr<'tcx>,
+) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
+    while let Some(parent) = get_parent_expr(cx, expr) {
+        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind {
+            expr = parent;
+        } else {
+            return Some((parent, expr));
+        }
+    }
+    None
+}
+
+/// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
+/// `Substs`, and arguments.
+fn get_callee_substs_and_args(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+) -> Option<(DefId, SubstsRef<'tcx>, &'tcx [Expr<'tcx>])> {
+    if_chain! {
+        if let ExprKind::Call(callee, args) = expr.kind;
+        let callee_ty = cx.typeck_results().expr_ty(callee);
+        if let ty::FnDef(callee_def_id, _) = callee_ty.kind();
+        then {
+            let substs = cx.typeck_results().node_substs(callee.hir_id);
+            return Some((*callee_def_id, substs, args));
+        }
+    }
+    if_chain! {
+        if let ExprKind::MethodCall(_, _, args, _) = expr.kind;
+        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        then {
+            let substs = cx.typeck_results().node_substs(expr.hir_id);
+            return Some((method_def_id, substs, args));
+        }
+    }
+    None
+}
+
+/// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type.
+fn get_input_traits_and_projections(
+    cx: &LateContext<'tcx>,
+    callee_def_id: DefId,
+    input: Ty<'tcx>,
+) -> (Vec<TraitPredicate<'tcx>>, Vec<ProjectionPredicate<'tcx>>) {
+    let mut trait_predicates = Vec::new();
+    let mut projection_predicates = Vec::new();
+    for (predicate, _) in cx.tcx.predicates_of(callee_def_id).predicates.iter() {
+        // `substs` should have 1 + n elements. The first is the type on the left hand side of an
+        // `as`. The remaining n are trait parameters.
+        let is_input_substs = |substs: SubstsRef<'tcx>| {
+            if_chain! {
+                if let Some(arg) = substs.iter().next();
+                if let GenericArgKind::Type(arg_ty) = arg.unpack();
+                if arg_ty == input;
+                then {
+                    true
+                } else {
+                    false
+                }
+            }
+        };
+        match predicate.kind().skip_binder() {
+            PredicateKind::Trait(trait_predicate) => {
+                if is_input_substs(trait_predicate.trait_ref.substs) {
+                    trait_predicates.push(trait_predicate);
+                }
+            },
+            PredicateKind::Projection(projection_predicate) => {
+                if is_input_substs(projection_predicate.projection_ty.substs) {
+                    projection_predicates.push(projection_predicate);
+                }
+            },
+            _ => {},
+        }
+    }
+    (trait_predicates, projection_predicates)
+}
+
+/// Composes two substitutions by applying the latter to the types of the former.
+fn compose_substs(cx: &LateContext<'tcx>, left: &[GenericArg<'tcx>], right: SubstsRef<'tcx>) -> Vec<GenericArg<'tcx>> {
+    left.iter()
+        .map(|arg| {
+            if let GenericArgKind::Type(arg_ty) = arg.unpack() {
+                let normalized_ty = cx.tcx.subst_and_normalize_erasing_regions(right, cx.param_env, arg_ty);
+                GenericArg::from(normalized_ty)
+            } else {
+                *arg
+            }
+        })
+        .collect()
+}
+
+/// 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 {
+    (method_name.as_str() == "cloned" || method_name.as_str() == "copied")
+        && is_diag_trait_item(cx, method_def_id, sym::Iterator)
+}
+
+/// Returns true if the named method can be used to convert the receiver to its "owned"
+/// representation.
+fn is_to_owned_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
+    is_clone_like(cx, &*method_name.as_str(), method_def_id)
+        || is_cow_into_owned(cx, method_name, method_def_id)
+        || is_to_string(cx, method_name, method_def_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.as_str() == "into_owned" && is_diag_item_method(cx, method_def_id, sym::Cow)
+}
+
+/// Returns true if the named method is `ToString::to_string`.
+fn is_to_string(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
+    method_name.as_str() == "to_string" && is_diag_trait_item(cx, method_def_id, sym::ToString)
+}