about summary refs log tree commit diff
path: root/clippy_lints/src/methods
diff options
context:
space:
mode:
authorPhilipp Krones <hello@philkrones.com>2022-09-09 13:36:26 +0200
committerPhilipp Krones <hello@philkrones.com>2022-09-09 13:36:26 +0200
commit98bf99e2f8cf8b357d63a67ce67d5fc5ceef8b3c (patch)
tree9737ff22b257f29282e7538d9ecb264451a3c1c0 /clippy_lints/src/methods
parent854f751b263dfac06dc3f635f8a9f92b8bc51da6 (diff)
downloadrust-98bf99e2f8cf8b357d63a67ce67d5fc5ceef8b3c.tar.gz
rust-98bf99e2f8cf8b357d63a67ce67d5fc5ceef8b3c.zip
Merge commit 'b52fb5234cd7c11ecfae51897a6f7fa52e8777fc' into clippyup
Diffstat (limited to 'clippy_lints/src/methods')
-rw-r--r--clippy_lints/src/methods/bind_instead_of_map.rs2
-rw-r--r--clippy_lints/src/methods/chars_cmp.rs2
-rw-r--r--clippy_lints/src/methods/clone_on_copy.rs6
-rw-r--r--clippy_lints/src/methods/filter_map.rs2
-rw-r--r--clippy_lints/src/methods/map_clone.rs2
-rw-r--r--clippy_lints/src/methods/mod.rs5
-rw-r--r--clippy_lints/src/methods/mut_mutex_lock.rs3
-rw-r--r--clippy_lints/src/methods/open_options.rs2
-rw-r--r--clippy_lints/src/methods/option_as_ref_deref.rs2
-rw-r--r--clippy_lints/src/methods/option_map_or_none.rs2
-rw-r--r--clippy_lints/src/methods/or_fun_call.rs13
-rw-r--r--clippy_lints/src/methods/suspicious_map.rs4
-rw-r--r--clippy_lints/src/methods/unnecessary_filter_map.rs11
-rw-r--r--clippy_lints/src/methods/unnecessary_fold.rs2
-rw-r--r--clippy_lints/src/methods/unnecessary_sort_by.rs2
-rw-r--r--clippy_lints/src/methods/unnecessary_to_owned.rs217
-rw-r--r--clippy_lints/src/methods/unwrap_or_else_default.rs24
17 files changed, 196 insertions, 105 deletions
diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs
index 2f117e4dcc3..22f5635a5bc 100644
--- a/clippy_lints/src/methods/bind_instead_of_map.rs
+++ b/clippy_lints/src/methods/bind_instead_of_map.rs
@@ -152,7 +152,7 @@ pub(crate) trait BindInsteadOfMap {
         match arg.kind {
             hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) => {
                 let closure_body = cx.tcx.hir().body(body);
-                let closure_expr = peel_blocks(&closure_body.value);
+                let closure_expr = peel_blocks(closure_body.value);
 
                 if Self::lint_closure_autofixable(cx, expr, recv, closure_expr, fn_decl_span) {
                     true
diff --git a/clippy_lints/src/methods/chars_cmp.rs b/clippy_lints/src/methods/chars_cmp.rs
index 51aec21527a..b2bc1ad5c9e 100644
--- a/clippy_lints/src/methods/chars_cmp.rs
+++ b/clippy_lints/src/methods/chars_cmp.rs
@@ -23,7 +23,7 @@ pub(super) fn check(
         if Some(id) == cx.tcx.lang_items().option_some_variant();
         then {
             let mut applicability = Applicability::MachineApplicable;
-            let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0].0).peel_refs();
+            let self_ty = cx.typeck_results().expr_ty_adjusted(args[0].0).peel_refs();
 
             if *self_ty.kind() != ty::Str {
                 return false;
diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs
index f5bead387d7..7ab6b84c207 100644
--- a/clippy_lints/src/methods/clone_on_copy.rs
+++ b/clippy_lints/src/methods/clone_on_copy.rs
@@ -21,7 +21,11 @@ pub(super) fn check(
     receiver: &Expr<'_>,
     args: &[Expr<'_>],
 ) {
-    let arg = if method_name == sym::clone && args.is_empty() { receiver } else { return };
+    let arg = if method_name == sym::clone && args.is_empty() {
+        receiver
+    } else {
+        return;
+    };
     if cx
         .typeck_results()
         .type_dependent_def_id(expr.hir_id)
diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs
index 9dc839afc62..9719b2f1c51 100644
--- a/clippy_lints/src/methods/filter_map.rs
+++ b/clippy_lints/src/methods/filter_map.rs
@@ -25,7 +25,7 @@ fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Sy
         },
         hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
             let body = cx.tcx.hir().body(body);
-            let closure_expr = peel_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, .. }, receiver, ..) => {
diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs
index e8442091fd3..8261ef5e1ce 100644
--- a/clippy_lints/src/methods/map_clone.rs
+++ b/clippy_lints/src/methods/map_clone.rs
@@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(
         if let hir::ExprKind::Closure(&hir::Closure{ body, .. }) = arg.kind;
         then {
             let closure_body = cx.tcx.hir().body(body);
-            let closure_expr = peel_blocks(&closure_body.value);
+            let closure_expr = peel_blocks(closure_body.value);
             match closure_body.params[0].pat.kind {
                 hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
                     hir::BindingAnnotation::NONE, .., name, None
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 48a9d6e7c32..41942b20ea1 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -825,8 +825,9 @@ declare_clippy_lint! {
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`,
-    /// etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or
-    /// `unwrap_or_default` instead.
+    /// `.or_insert(foo(..))` etc., and suggests to use `.or_else(|| foo(..))`,
+    /// `.unwrap_or_else(|| foo(..))`, `.unwrap_or_default()` or `.or_default()`
+    /// etc. instead.
     ///
     /// ### Why is this bad?
     /// The function will always be called and potentially
diff --git a/clippy_lints/src/methods/mut_mutex_lock.rs b/clippy_lints/src/methods/mut_mutex_lock.rs
index bd8458a222e..b9593b3687d 100644
--- a/clippy_lints/src/methods/mut_mutex_lock.rs
+++ b/clippy_lints/src/methods/mut_mutex_lock.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{expr_custom_deref_adjustment, ty::is_type_diagnostic_item};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, Mutability};
@@ -11,6 +11,7 @@ use super::MUT_MUTEX_LOCK;
 
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) {
     if_chain! {
+        if matches!(expr_custom_deref_adjustment(cx, recv), None | Some(Mutability::Mut));
         if let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind();
         if let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id);
         if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
diff --git a/clippy_lints/src/methods/open_options.rs b/clippy_lints/src/methods/open_options.rs
index 903fa306f93..597af853dc6 100644
--- a/clippy_lints/src/methods/open_options.rs
+++ b/clippy_lints/src/methods/open_options.rs
@@ -40,7 +40,7 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec
         let obj_ty = cx.typeck_results().expr_ty(receiver).peel_refs();
 
         // Only proceed if this is a call on some object of type std::fs::OpenOptions
-        if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 1 {
+        if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && !arguments.is_empty() {
             let argument_option = match arguments[0].kind {
                 ExprKind::Lit(ref span) => {
                     if let Spanned {
diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs
index 81c67b4ca6a..c409268de76 100644
--- a/clippy_lints/src/methods/option_as_ref_deref.rs
+++ b/clippy_lints/src/methods/option_as_ref_deref.rs
@@ -53,7 +53,7 @@ pub(super) fn check<'tcx>(
             }),
         hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
             let closure_body = cx.tcx.hir().body(body);
-            let closure_expr = peel_blocks(&closure_body.value);
+            let closure_expr = peel_blocks(closure_body.value);
 
             match &closure_expr.kind {
                 hir::ExprKind::MethodCall(_, receiver, [], _) => {
diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs
index 5a39b82b027..6657cdccd01 100644
--- a/clippy_lints/src/methods/option_map_or_none.rs
+++ b/clippy_lints/src/methods/option_map_or_none.rs
@@ -74,7 +74,7 @@ pub(super) fn check<'tcx>(
             if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = map_arg.kind;
             let arg_snippet = snippet(cx, fn_decl_span, "..");
             let body = cx.tcx.hir().body(body);
-            if let Some((func, [arg_char])) = reduce_unit_expression(&body.value);
+            if let Some((func, [arg_char])) = reduce_unit_expression(body.value);
             if let Some(id) = path_def_id(cx, func).map(|ctor_id| cx.tcx.parent(ctor_id));
             if Some(id) == cx.tcx.lang_items().option_some_variant();
             then {
diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs
index 76876d86629..b43b9258c47 100644
--- a/clippy_lints/src/methods/or_fun_call.rs
+++ b/clippy_lints/src/methods/or_fun_call.rs
@@ -23,7 +23,8 @@ pub(super) fn check<'tcx>(
     receiver: &'tcx hir::Expr<'_>,
     args: &'tcx [hir::Expr<'_>],
 ) {
-    /// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`.
+    /// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`,
+    /// `or_insert(T::new())` or `or_insert(T::default())`.
     #[allow(clippy::too_many_arguments)]
     fn check_unwrap_or_default(
         cx: &LateContext<'_>,
@@ -43,7 +44,11 @@ pub(super) fn check<'tcx>(
 
         if_chain! {
             if !or_has_args;
-            if name == "unwrap_or";
+            if let Some(sugg) = match name {
+                "unwrap_or" => Some("unwrap_or_default"),
+                "or_insert" => Some("or_default"),
+                _ => None,
+            };
             if let hir::ExprKind::Path(ref qpath) = fun.kind;
             if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default);
             let path = last_path_segment(qpath).ident.name;
@@ -59,7 +64,7 @@ pub(super) fn check<'tcx>(
                     method_span.with_hi(span.hi()),
                     &format!("use of `{}` followed by a call to `{}`", name, path),
                     "try this",
-                    "unwrap_or_default()".to_string(),
+                    format!("{}()", sugg),
                     Applicability::MachineApplicable,
                 );
 
@@ -83,7 +88,7 @@ pub(super) fn check<'tcx>(
         fun_span: Option<Span>,
     ) {
         // (path, fn_has_argument, methods, suffix)
-        static KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [
+        const KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [
             (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
             (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
             (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
diff --git a/clippy_lints/src/methods/suspicious_map.rs b/clippy_lints/src/methods/suspicious_map.rs
index 9c3375bf35e..851cdf54455 100644
--- a/clippy_lints/src/methods/suspicious_map.rs
+++ b/clippy_lints/src/methods/suspicious_map.rs
@@ -15,9 +15,9 @@ pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, count_recv: &hi
         if let Some(def_id) = cx.tcx.hir().opt_local_def_id(closure.hir_id);
         if let Some(body_id) = cx.tcx.hir().maybe_body_owned_by(def_id);
         let closure_body = cx.tcx.hir().body(body_id);
-        if !cx.typeck_results().expr_ty(&closure_body.value).is_unit();
+        if !cx.typeck_results().expr_ty(closure_body.value).is_unit();
         then {
-            if let Some(map_mutated_vars) = mutated_variables(&closure_body.value, cx) {
+            if let Some(map_mutated_vars) = mutated_variables(closure_body.value, cx) {
                 // A variable is used mutably inside of the closure. Suppress the lint.
                 if !map_mutated_vars.is_empty() {
                     return;
diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs
index bafa6fc584d..4e8c201f470 100644
--- a/clippy_lints/src/methods/unnecessary_filter_map.rs
+++ b/clippy_lints/src/methods/unnecessary_filter_map.rs
@@ -21,14 +21,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<
     if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = arg.kind {
         let body = cx.tcx.hir().body(body);
         let arg_id = body.params[0].pat.hir_id;
-        let mutates_arg =
-            mutated_variables(&body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id));
-        let (clone_or_copy_needed, _) = clone_or_copy_needed(cx, body.params[0].pat, &body.value);
+        let mutates_arg = mutated_variables(body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id));
+        let (clone_or_copy_needed, _) = clone_or_copy_needed(cx, body.params[0].pat, body.value);
 
-        let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, &body.value);
+        let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, body.value);
 
         let mut return_visitor = ReturnVisitor::new(cx, arg_id);
-        return_visitor.visit_expr(&body.value);
+        return_visitor.visit_expr(body.value);
         found_mapping |= return_visitor.found_mapping;
         found_filtering |= return_visitor.found_filtering;
 
@@ -36,7 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<
         let sugg = if !found_filtering {
             if name == "filter_map" { "map" } else { "map(..).next()" }
         } else if !found_mapping && !mutates_arg && (!clone_or_copy_needed || is_copy(cx, in_ty)) {
-            match cx.typeck_results().expr_ty(&body.value).kind() {
+            match cx.typeck_results().expr_ty(body.value).kind() {
                 ty::Adt(adt, subst)
                     if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && in_ty == subst.type_at(0) =>
                 {
diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs
index c3531d4d051..c17ef6809f9 100644
--- a/clippy_lints/src/methods/unnecessary_fold.rs
+++ b/clippy_lints/src/methods/unnecessary_fold.rs
@@ -31,7 +31,7 @@ pub(super) fn check(
             // Extract the body of the closure passed to fold
             if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = acc.kind;
             let closure_body = cx.tcx.hir().body(body);
-            let closure_expr = peel_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_sort_by.rs b/clippy_lints/src/methods/unnecessary_sort_by.rs
index 6f25acca1de..ed5a75b0f3c 100644
--- a/clippy_lints/src/methods/unnecessary_sort_by.rs
+++ b/clippy_lints/src/methods/unnecessary_sort_by.rs
@@ -131,7 +131,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Exp
         ] = &closure_body.params;
         if let ExprKind::MethodCall(method_path, left_expr, [right_expr], _) = closure_body.value.kind;
         if method_path.ident.name == sym::cmp;
-        if is_trait_method(cx, &closure_body.value, sym::Ord);
+        if is_trait_method(cx, closure_body.value, sym::Ord);
         then {
             let (closure_body, closure_arg, reverse) = if mirrored_exprs(
                 left_expr,
diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs
index 85da97a39f9..763bfafecef 100644
--- a/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -1,21 +1,25 @@
 use super::implicit_clone::is_clone_like;
 use super::unnecessary_iter_cloned::{self, is_into_iter};
+use crate::rustc_middle::ty::Subst;
 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, is_type_diagnostic_item, peel_mid_ty_refs,
-};
-use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
+use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
+use clippy_utils::visitors::find_all_ret_expressions;
+use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
 use clippy_utils::{meets_msrv, msrvs};
 use rustc_errors::Applicability;
-use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
+use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, Node};
+use rustc_infer::infer::TyCtxtInferExt;
 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_middle::ty::EarlyBinder;
+use rustc_middle::ty::{self, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
 use rustc_semver::RustcVersion;
 use rustc_span::{sym, Symbol};
+use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
+use rustc_typeck::check::{FnCtxt, Inherited};
 use std::cmp::max;
 
 use super::UNNECESSARY_TO_OWNED;
@@ -34,7 +38,7 @@ pub fn check<'tcx>(
         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) {
+            } else if is_to_owned_like(cx, expr, 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
@@ -246,17 +250,12 @@ fn check_other_call_arg<'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_receiver, call_args)) = get_callee_substs_and_args(cx, maybe_call);
+        if let Some((callee_def_id, _, recv, call_args)) = get_callee_substs_and_args(cx, maybe_call);
         let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
-        let index = if let Some(call_receiver) = call_receiver {
-            std::iter::once(call_receiver).chain(call_args.iter()).position(|arg| arg.hir_id == maybe_arg.hir_id)
-        } else {
-            call_args.iter().position(|arg| arg.hir_id == maybe_arg.hir_id)
-        };
-        if let Some(i) = index;
+        if let Some(i) = recv.into_iter().chain(call_args).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 (trait_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()
@@ -264,52 +263,13 @@ fn check_other_call_arg<'tcx>(
             .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);
+        if trait_predicate.def_id() == deref_trait_id || trait_predicate.def_id() == as_ref_trait_id;
         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.term);
-                implements_trait(cx, receiver_ty, deref_trait_id, &[])
-                    && get_associated_type(cx, receiver_ty, deref_trait_id, "Target")
-                        .map_or(false, |ty| ty::TermKind::Ty(ty) == normalized_ty.unpack())
-            } 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,
-            );
-            // if `expr` is a `String` and generic target is [u8], skip
-            // (https://github.com/rust-lang/rust-clippy/issues/9317).
-            if let [subst] = composed_substs[..]
-                && let GenericArgKind::Type(arg_ty) = subst.unpack()
-                && arg_ty.is_slice()
-                && let inner_ty = arg_ty.builtin_index().unwrap()
-                && let ty::Uint(ty::UintTy::U8) = inner_ty.kind()
-                && let self_ty = cx.typeck_results().expr_ty(expr).peel_refs()
-                && is_type_diagnostic_item(cx, self_ty, sym::String) {
-                false
-            } else {
-                implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
-            }
-        } else {
-            false
-        };
+        if can_change_type(cx, maybe_arg, receiver_ty);
         // 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 the trait is `AsRef` and the input type variable `T` occurs in the output type, then
-        // `T` must not be instantiated with a reference
-        // (https://github.com/rust-lang/rust-clippy/issues/8507).
-        if (n_refs == 0 && !receiver_ty.is_ref())
-            || trait_predicate.def_id() != as_ref_trait_id
-            || !fn_sig.output().contains(input);
         if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
         then {
             span_lint_and_sugg(
@@ -359,11 +319,11 @@ fn get_callee_substs_and_args<'tcx>(
         }
     }
     if_chain! {
-        if let ExprKind::MethodCall(_, receiver, args, _) = expr.kind;
+        if let ExprKind::MethodCall(_, recv, 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, Some(receiver), args));
+            return Some((method_def_id, substs, Some(recv), args));
         }
     }
     None
@@ -395,22 +355,103 @@ fn get_input_traits_and_projections<'tcx>(
     (trait_predicates, projection_predicates)
 }
 
-/// Composes two substitutions by applying the latter to the types of the former.
-fn compose_substs<'tcx>(
-    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
+fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<'a>) -> bool {
+    for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
+        match node {
+            Node::Stmt(_) => return true,
+            Node::Block(..) => continue,
+            Node::Item(item) => {
+                if let ItemKind::Fn(_, _, body_id) = &item.kind
+                && let output_ty = return_ty(cx, item.hir_id())
+                && let local_def_id = cx.tcx.hir().local_def_id(item.hir_id())
+                && Inherited::build(cx.tcx, local_def_id).enter(|inherited| {
+                    let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.hir_id());
+                    fn_ctxt.can_coerce(ty, output_ty)
+                }) {
+                    if has_lifetime(output_ty) && has_lifetime(ty) {
+                        return false;
+                    }
+                    let body = cx.tcx.hir().body(*body_id);
+                    let body_expr = &body.value;
+                    let mut count = 0;
+                    return find_all_ret_expressions(cx, body_expr, |_| { count += 1; count <= 1 });
+                }
             }
-        })
-        .collect()
+            Node::Expr(parent_expr) => {
+                if let Some((callee_def_id, call_substs, recv, call_args)) = get_callee_substs_and_args(cx, parent_expr)
+                {
+                    let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
+                    if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id)
+                        && let Some(param_ty) = fn_sig.inputs().get(arg_index)
+                        && let ty::Param(ParamTy { index: param_index , ..}) = param_ty.kind()
+                    {
+                        if fn_sig
+                            .inputs()
+                            .iter()
+                            .enumerate()
+                            .filter(|(i, _)| *i != arg_index)
+                            .any(|(_, ty)| ty.contains(*param_ty))
+                        {
+                            return false;
+                        }
+
+                        let mut trait_predicates = cx.tcx.param_env(callee_def_id)
+                            .caller_bounds().iter().filter(|predicate| {
+                            if let PredicateKind::Trait(trait_predicate) =  predicate.kind().skip_binder()
+                                && trait_predicate.trait_ref.self_ty() == *param_ty {
+                                    true
+                                } else {
+                                false
+                            }
+                        });
+
+                        let new_subst = cx.tcx.mk_substs(
+                            call_substs.iter()
+                                .enumerate()
+                                .map(|(i, t)|
+                                     if i == (*param_index as usize) {
+                                         GenericArg::from(ty)
+                                     } else {
+                                         t
+                                     }));
+
+                        if trait_predicates.any(|predicate| {
+                            let predicate = EarlyBinder(predicate).subst(cx.tcx, new_subst);
+                            let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate);
+                            !cx.tcx
+                                .infer_ctxt()
+                                .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
+                        }) {
+                            return false;
+                        }
+
+                        let output_ty = fn_sig.output();
+                        if output_ty.contains(*param_ty) {
+                            if let Ok(new_ty)  = cx.tcx.try_subst_and_normalize_erasing_regions(
+                                new_subst, cx.param_env, output_ty) {
+                                expr = parent_expr;
+                                ty = new_ty;
+                                continue;
+                            }
+                            return false;
+                        }
+
+                        return true;
+                    }
+                } else if let ExprKind::Block(..) = parent_expr.kind {
+                    continue;
+                }
+                return false;
+            },
+            _ => return false,
+        }
+    }
+
+    false
+}
+
+fn has_lifetime(ty: Ty<'_>) -> bool {
+    ty.walk().any(|t| matches!(t.unpack(), GenericArgKind::Lifetime(_)))
 }
 
 /// Returns true if the named method is `Iterator::cloned` or `Iterator::copied`.
@@ -421,10 +462,10 @@ fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id:
 
 /// 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 {
+fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, 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)
+        || is_to_string_on_string_like(cx, call_expr, method_name, method_def_id)
 }
 
 /// Returns true if the named method is `Cow::into_owned`.
@@ -432,7 +473,27 @@ fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: D
     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 == sym::to_string && is_diag_trait_item(cx, method_def_id, sym::ToString)
+/// Returns true if the named method is `ToString::to_string` and it's called on a type that
+/// is string-like i.e. implements `AsRef<str>` or `Deref<str>`.
+fn is_to_string_on_string_like<'a>(
+    cx: &LateContext<'_>,
+    call_expr: &'a Expr<'a>,
+    method_name: Symbol,
+    method_def_id: DefId,
+) -> bool {
+    if method_name != sym::to_string || !is_diag_trait_item(cx, method_def_id, sym::ToString) {
+        return false;
+    }
+
+    if let Some(substs) = cx.typeck_results().node_substs_opt(call_expr.hir_id)
+        && let [generic_arg] = substs.as_slice()
+        && let GenericArgKind::Type(ty) = generic_arg.unpack()
+        && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref)
+        && let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef)
+        && (implements_trait(cx, ty, deref_trait_id, &[cx.tcx.types.str_.into()]) ||
+            implements_trait(cx, ty, as_ref_trait_id, &[cx.tcx.types.str_.into()])) {
+            true
+        } else {
+            false
+        }
 }
diff --git a/clippy_lints/src/methods/unwrap_or_else_default.rs b/clippy_lints/src/methods/unwrap_or_else_default.rs
index f3af281d6ca..045f739e64d 100644
--- a/clippy_lints/src/methods/unwrap_or_else_default.rs
+++ b/clippy_lints/src/methods/unwrap_or_else_default.rs
@@ -5,10 +5,11 @@ use clippy_utils::{
     diagnostics::span_lint_and_sugg, is_default_equivalent_call, source::snippet_with_applicability,
     ty::is_type_diagnostic_item,
 };
+use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
-use rustc_span::sym;
+use rustc_span::{sym, symbol};
 
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
@@ -25,7 +26,7 @@ pub(super) fn check<'tcx>(
 
     if_chain! {
         if is_option || is_result;
-        if is_default_equivalent_call(cx, u_arg);
+        if closure_body_returns_empty_to_string(cx, u_arg) || is_default_equivalent_call(cx, u_arg);
         then {
             let mut applicability = Applicability::MachineApplicable;
 
@@ -44,3 +45,22 @@ pub(super) fn check<'tcx>(
         }
     }
 }
+
+fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool {
+    if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = e.kind {
+        let body = cx.tcx.hir().body(body);
+
+        if body.params.is_empty()
+            && let hir::Expr{ kind, .. } = &body.value
+            && let hir::ExprKind::MethodCall(hir::PathSegment {ident, ..}, self_arg, _, _) = kind
+            && ident == &symbol::Ident::from_str("to_string")
+            && let hir::Expr{ kind, .. } = self_arg
+            && let hir::ExprKind::Lit(lit) = kind
+            && let LitKind::Str(symbol::kw::Empty, _) = lit.node
+        {
+            return true;
+        }
+    }
+
+    false
+}