about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md5
-rw-r--r--Cargo.toml2
-rw-r--r--clippy_lints/Cargo.toml2
-rw-r--r--clippy_lints/src/casts/cast_ptr_alignment.rs6
-rw-r--r--clippy_lints/src/declared_lints.rs1
-rw-r--r--clippy_lints/src/dereference.rs3
-rw-r--r--clippy_lints/src/implied_bounds_in_impls.rs202
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_lints/src/matches/mod.rs2
-rw-r--r--clippy_lints/src/matches/try_err.rs2
-rw-r--r--clippy_lints/src/methods/clone_on_copy.rs2
-rw-r--r--clippy_lints/src/methods/iter_overeager_cloned.rs2
-rw-r--r--clippy_lints/src/methods/mod.rs22
-rw-r--r--clippy_lints/src/methods/str_splitn.rs2
-rw-r--r--clippy_lints/src/missing_inline.rs1
-rw-r--r--clippy_lints/src/needless_question_mark.rs2
-rw-r--r--clippy_lints/src/ptr.rs25
-rw-r--r--clippy_lints/src/question_mark_used.rs2
-rw-r--r--clippy_lints/src/redundant_closure_call.rs2
-rw-r--r--clippy_lints/src/returns.rs2
-rw-r--r--clippy_lints/src/unit_types/unit_arg.rs2
-rw-r--r--clippy_lints/src/useless_conversion.rs2
-rw-r--r--clippy_utils/Cargo.toml2
-rw-r--r--clippy_utils/src/check_proc_macro.rs2
-rw-r--r--clippy_utils/src/hir_utils.rs3
-rw-r--r--clippy_utils/src/lib.rs11
-rw-r--r--clippy_utils/src/paths.rs3
-rw-r--r--clippy_utils/src/qualify_min_const_fn.rs6
-rw-r--r--clippy_utils/src/ty.rs7
-rw-r--r--clippy_utils/src/visitors.rs2
-rw-r--r--declare_clippy_lint/Cargo.toml2
-rw-r--r--rust-toolchain2
-rw-r--r--tests/ui/crashes/ice-11337.rs9
-rw-r--r--tests/ui/implied_bounds_in_impls.fixed68
-rw-r--r--tests/ui/implied_bounds_in_impls.rs68
-rw-r--r--tests/ui/implied_bounds_in_impls.stderr123
-rw-r--r--tests/ui/iter_overeager_cloned.fixed6
-rw-r--r--tests/ui/iter_overeager_cloned.rs2
-rw-r--r--tests/ui/iter_overeager_cloned.stderr18
39 files changed, 571 insertions, 56 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4e33cb7b457..92cc19edb05 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,7 +10,7 @@ document.
 
 ## Rust 1.72
 
-Current beta, released 2023-08-24
+Current stable, released 2023-08-24
 
 [View all 131 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-05-22T14%3A53%3A59Z..2023-07-01T22%3A57%3A20Z+base%3Amaster)
 
@@ -101,7 +101,7 @@ Current beta, released 2023-08-24
 
 ## Rust 1.71
 
-Current stable, released 2023-07-13
+Released 2023-07-13
 
 Note: Clippy will use a shorter changelog format from now on, if you want a detailed list of
 all changes, please check out the list of merged pull requests.
@@ -4985,6 +4985,7 @@ Released 2018-09-13
 [`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return
 [`implicit_saturating_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_add
 [`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub
+[`implied_bounds_in_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#implied_bounds_in_impls
 [`impossible_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#impossible_comparisons
 [`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
 [`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
diff --git a/Cargo.toml b/Cargo.toml
index 1ab6d7e118f..cd04c78ef9e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy"
-version = "0.1.73"
+version = "0.1.74"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml
index 11136867ff0..c0a9f466e7b 100644
--- a/clippy_lints/Cargo.toml
+++ b/clippy_lints/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_lints"
-version = "0.1.73"
+version = "0.1.74"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs
index 5bf467efa0f..1de69122101 100644
--- a/clippy_lints/src/casts/cast_ptr_alignment.rs
+++ b/clippy_lints/src/casts/cast_ptr_alignment.rs
@@ -5,6 +5,7 @@ use rustc_hir::{Expr, ExprKind, GenericArg};
 use rustc_lint::LateContext;
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty::{self, Ty};
+use rustc_span::sym;
 
 use super::CAST_PTR_ALIGNMENT;
 
@@ -76,13 +77,14 @@ fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
         ExprKind::Call(func, [arg, ..]) if arg.hir_id == e.hir_id => {
             static PATHS: &[&[&str]] = &[
                 paths::PTR_READ_UNALIGNED.as_slice(),
-                paths::PTR_WRITE_UNALIGNED.as_slice(),
                 paths::PTR_UNALIGNED_VOLATILE_LOAD.as_slice(),
                 paths::PTR_UNALIGNED_VOLATILE_STORE.as_slice(),
             ];
+
             if let ExprKind::Path(path) = &func.kind
                 && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id()
-                && match_any_def_paths(cx, def_id, PATHS).is_some()
+                && (match_any_def_paths(cx, def_id, PATHS).is_some()
+                    || cx.tcx.is_diagnostic_item(sym::ptr_write_unaligned, def_id))
             {
                 true
             } else {
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index 1be9720fbbf..f73f1ed18f8 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -209,6 +209,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::implicit_return::IMPLICIT_RETURN_INFO,
     crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO,
     crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO,
+    crate::implied_bounds_in_impls::IMPLIED_BOUNDS_IN_IMPLS_INFO,
     crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO,
     crate::incorrect_impls::INCORRECT_CLONE_IMPL_ON_COPY_TYPE_INFO,
     crate::incorrect_impls::INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE_INFO,
diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs
index bc011a6c354..58c27855000 100644
--- a/clippy_lints/src/dereference.rs
+++ b/clippy_lints/src/dereference.rs
@@ -802,7 +802,8 @@ fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> boo
         match parent.kind {
             ExprKind::Call(child, _) | ExprKind::MethodCall(_, child, _, _) | ExprKind::Index(child, _, _)
                 if child.hir_id == e.hir_id => true,
-            ExprKind::Field(_, _) | ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar) => true,
+            ExprKind::Match(.., MatchSource::TryDesugar(_) | MatchSource::AwaitDesugar)
+                | ExprKind::Field(_, _) => true,
             _ => false,
         }
     } else {
diff --git a/clippy_lints/src/implied_bounds_in_impls.rs b/clippy_lints/src/implied_bounds_in_impls.rs
new file mode 100644
index 00000000000..c6d1acad3a0
--- /dev/null
+++ b/clippy_lints/src/implied_bounds_in_impls.rs
@@ -0,0 +1,202 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::intravisit::FnKind;
+use rustc_hir::{
+    Body, FnDecl, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, ItemKind, TraitBoundModifier, TraitItem,
+    TraitItemKind, TyKind,
+};
+use rustc_hir_analysis::hir_ty_to_ty;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{self, ClauseKind, TyCtxt};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Looks for bounds in `impl Trait` in return position that are implied by other bounds.
+    /// This can happen when a trait is specified that another trait already has as a supertrait
+    /// (e.g. `fn() -> impl Deref + DerefMut<Target = i32>` has an unnecessary `Deref` bound,
+    /// because `Deref` is a supertrait of `DerefMut`)
+    ///
+    /// ### Why is this bad?
+    /// Specifying more bounds than necessary adds needless complexity for the reader.
+    ///
+    /// ### Limitations
+    /// This lint does not check for implied bounds transitively. Meaning that
+    /// it does't check for implied bounds from supertraits of supertraits
+    /// (e.g. `trait A {} trait B: A {} trait C: B {}`, then having an `fn() -> impl A + C`)
+    ///
+    /// ### Example
+    /// ```rust
+    /// # use std::ops::{Deref,DerefMut};
+    /// fn f() -> impl Deref<Target = i32> + DerefMut<Target = i32> {
+    /// //             ^^^^^^^^^^^^^^^^^^^ unnecessary bound, already implied by the `DerefMut` trait bound
+    ///     Box::new(123)
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # use std::ops::{Deref,DerefMut};
+    /// fn f() -> impl DerefMut<Target = i32> {
+    ///     Box::new(123)
+    /// }
+    /// ```
+    #[clippy::version = "1.73.0"]
+    pub IMPLIED_BOUNDS_IN_IMPLS,
+    complexity,
+    "specifying bounds that are implied by other bounds in `impl Trait` type"
+}
+declare_lint_pass!(ImpliedBoundsInImpls => [IMPLIED_BOUNDS_IN_IMPLS]);
+
+/// This function tries to, for all type parameters in a supertype predicate `GenericTrait<U>`,
+/// check if the substituted type in the implied-by bound matches with what's subtituted in the
+/// implied type.
+///
+/// Consider this example.
+/// ```rust,ignore
+/// trait GenericTrait<T> {}
+/// trait GenericSubTrait<T, U, V>: GenericTrait<U> {}
+///                                              ^ trait_predicate_args: [Self#0, U#2]
+/// impl GenericTrait<i32> for () {}
+/// impl GenericSubTrait<(), i32, ()> for () {}
+/// impl GenericSubTrait<(), [u8; 8], ()> for () {}
+///
+/// fn f() -> impl GenericTrait<i32> + GenericSubTrait<(), [u8; 8], ()> {
+///                             ^^^ implied_args       ^^^^^^^^^^^^^^^ implied_by_args
+///                                                                    (we are interested in `[u8; 8]` specifically, as that
+///                                                                     is what `U` in `GenericTrait<U>` is substituted with)
+///     ()
+/// }
+/// ```
+/// Here i32 != [u8; 8], so this will return false.
+fn is_same_generics(
+    tcx: TyCtxt<'_>,
+    trait_predicate_args: &[ty::GenericArg<'_>],
+    implied_by_args: &[GenericArg<'_>],
+    implied_args: &[GenericArg<'_>],
+) -> bool {
+    trait_predicate_args
+        .iter()
+        .enumerate()
+        .skip(1) // skip `Self` implicit arg
+        .all(|(arg_index, arg)| {
+            if let Some(ty) = arg.as_type()
+                && let &ty::Param(ty::ParamTy{ index, .. }) = ty.kind()
+                // Since `trait_predicate_args` and type params in traits start with `Self=0`
+                // and generic argument lists `GenericTrait<i32>` don't have `Self`,
+                // we need to subtract 1 from the index.
+                && let GenericArg::Type(ty_a) = implied_by_args[index as usize - 1]
+                && let GenericArg::Type(ty_b) = implied_args[arg_index - 1]
+            {
+                hir_ty_to_ty(tcx, ty_a) == hir_ty_to_ty(tcx, ty_b)
+            } else {
+                false
+            }
+        })
+}
+
+fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
+    if let FnRetTy::Return(ty) = decl.output
+        &&let TyKind::OpaqueDef(item_id, ..) = ty.kind
+        && let item = cx.tcx.hir().item(item_id)
+        && let ItemKind::OpaqueTy(opaque_ty) = item.kind
+        // Very often there is only a single bound, e.g. `impl Deref<..>`, in which case
+        // we can avoid doing a bunch of stuff unnecessarily.
+        && opaque_ty.bounds.len() > 1
+    {
+        // Get all the (implied) trait predicates in the bounds.
+        // For `impl Deref + DerefMut` this will contain [`Deref`].
+        // The implied `Deref` comes from `DerefMut` because `trait DerefMut: Deref {}`.
+        // N.B. (G)ATs are fine to disregard, because they must be the same for all of its supertraits.
+        // Example:
+        // `impl Deref<Target = i32> + DerefMut<Target = u32>` is not allowed.
+        // `DerefMut::Target` needs to match `Deref::Target`.
+        let implied_bounds: Vec<_> = opaque_ty.bounds.iter().filter_map(|bound| {
+            if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound
+                && let [.., path]  = poly_trait.trait_ref.path.segments
+                && poly_trait.bound_generic_params.is_empty()
+                && let Some(trait_def_id) = path.res.opt_def_id()
+                && let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates
+                && !predicates.is_empty() // If the trait has no supertrait, there is nothing to add.
+            {
+                Some((bound.span(), path.args.map_or([].as_slice(), |a| a.args), predicates))
+            } else {
+                None
+            }
+        }).collect();
+
+        // Lint all bounds in the `impl Trait` type that are also in the `implied_bounds` vec.
+        // This involves some extra logic when generic arguments are present, since
+        // simply comparing trait `DefId`s won't be enough. We also need to compare the generics.
+        for (index, bound) in opaque_ty.bounds.iter().enumerate() {
+            if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound
+                && let [.., path] = poly_trait.trait_ref.path.segments
+                && let implied_args = path.args.map_or([].as_slice(), |a| a.args)
+                && let Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id()
+                && let Some(implied_by_span) = implied_bounds.iter().find_map(|&(span, implied_by_args, preds)| {
+                    preds.iter().find_map(|(clause, _)| {
+                        if let ClauseKind::Trait(tr) = clause.kind().skip_binder()
+                            && tr.def_id() == def_id
+                            && is_same_generics(cx.tcx, tr.trait_ref.args, implied_by_args, implied_args)
+                        {
+                            Some(span)
+                        } else {
+                            None
+                        }
+                    })
+                })
+            {
+                let implied_by = snippet(cx, implied_by_span, "..");
+                span_lint_and_then(
+                    cx, IMPLIED_BOUNDS_IN_IMPLS,
+                    poly_trait.span,
+                    &format!("this bound is already specified as the supertrait of `{implied_by}`"),
+                    |diag| {
+                        // If we suggest removing a bound, we may also need extend the span
+                        // to include the `+` token, so we don't end up with something like `impl + B`
+
+                        let implied_span_extended = if let Some(next_bound) = opaque_ty.bounds.get(index + 1) {
+                            poly_trait.span.to(next_bound.span().shrink_to_lo())
+                        } else {
+                            poly_trait.span
+                        };
+
+                        diag.span_suggestion_with_style(
+                            implied_span_extended,
+                            "try removing this bound",
+                            "",
+                            Applicability::MachineApplicable,
+                            SuggestionStyle::ShowAlways
+                        );
+                    }
+                );
+            }
+        }
+    }
+}
+
+impl LateLintPass<'_> for ImpliedBoundsInImpls {
+    fn check_fn(
+        &mut self,
+        cx: &LateContext<'_>,
+        _: FnKind<'_>,
+        decl: &FnDecl<'_>,
+        _: &Body<'_>,
+        _: Span,
+        _: LocalDefId,
+    ) {
+        check(cx, decl);
+    }
+    fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
+        if let TraitItemKind::Fn(sig, ..) = &item.kind {
+            check(cx, sig.decl);
+        }
+    }
+    fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &ImplItem<'_>) {
+        if let ImplItemKind::Fn(sig, ..) = &item.kind {
+            check(cx, sig.decl);
+        }
+    }
+}
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index f50019f3cf7..ab71bfbdc58 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -152,6 +152,7 @@ mod implicit_hasher;
 mod implicit_return;
 mod implicit_saturating_add;
 mod implicit_saturating_sub;
+mod implied_bounds_in_impls;
 mod inconsistent_struct_constructor;
 mod incorrect_impls;
 mod index_refutable_slice;
@@ -1097,6 +1098,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::new(redundant_locals::RedundantLocals));
     store.register_late_pass(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns));
     store.register_late_pass(|_| Box::<reserve_after_initialization::ReserveAfterInitialization>::default());
+    store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs
index 6d16d188754..930386a60aa 100644
--- a/clippy_lints/src/matches/mod.rs
+++ b/clippy_lints/src/matches/mod.rs
@@ -1038,7 +1038,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
                 wild_in_or_pats::check(cx, arms);
             }
 
-            if source == MatchSource::TryDesugar {
+            if let MatchSource::TryDesugar(_) = source {
                 try_err::check(cx, expr, ex);
             }
 
diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs
index 99a748489b4..0fd6f533db0 100644
--- a/clippy_lints/src/matches/try_err.rs
+++ b/clippy_lints/src/matches/try_err.rs
@@ -80,7 +80,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine
 
 /// Finds function return type by examining return expressions in match arms.
 fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option<Ty<'tcx>> {
-    if let ExprKind::Match(_, arms, MatchSource::TryDesugar) = expr {
+    if let ExprKind::Match(_, arms, MatchSource::TryDesugar(_)) = expr {
         for arm in *arms {
             if let ExprKind::Ret(Some(ret)) = arm.body.kind {
                 return Some(cx.typeck_results().expr_ty(ret));
diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs
index 7eb325ee7b5..eb4f003d38a 100644
--- a/clippy_lints/src/methods/clone_on_copy.rs
+++ b/clippy_lints/src/methods/clone_on_copy.rs
@@ -64,7 +64,7 @@ pub(super) fn check(
                     ExprKind::Path(QPath::LangItem(rustc_hir::LangItem::TryTraitBranch, _, _))
                 ),
                 ExprKind::MethodCall(_, self_arg, ..) if expr.hir_id == self_arg.hir_id => true,
-                ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
+                ExprKind::Match(_, _, MatchSource::TryDesugar(_) | MatchSource::AwaitDesugar)
                 | ExprKind::Field(..)
                 | ExprKind::Index(..) => true,
                 _ => false,
diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs
index ee405a3e30c..a49dd98db87 100644
--- a/clippy_lints/src/methods/iter_overeager_cloned.rs
+++ b/clippy_lints/src/methods/iter_overeager_cloned.rs
@@ -21,7 +21,7 @@ pub(super) enum Op<'a> {
     RmCloned,
 
     // rm `.cloned()`
-    // e.g. `map` `for_each`
+    // e.g. `map` `for_each` `all` `any`
     NeedlessMove(&'a str, &'a Expr<'a>),
 
     // later `.cloned()`
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 5075137caa0..5fb0d9f0a57 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -3873,6 +3873,12 @@ impl Methods {
                 ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
                     zst_offset::check(cx, expr, recv);
                 },
+                ("all", [arg]) => {
+                    if let Some(("cloned", recv2, [], _, _)) = method_call(recv) {
+                        iter_overeager_cloned::check(cx, expr, recv, recv2,
+                                iter_overeager_cloned::Op::NeedlessMove(name, arg), false);
+                    }
+                }
                 ("and_then", [arg]) => {
                     let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg);
                     let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg);
@@ -3880,12 +3886,16 @@ impl Methods {
                         unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
                     }
                 },
-                ("any", [arg]) if let ExprKind::Closure(arg) = arg.kind
-                    && let body = cx.tcx.hir().body(arg.body)
-                    && let [param] = body.params
-                    && let Some(("chars", recv, _, _, _)) = method_call(recv) =>
-                {
-                    string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
+                ("any", [arg]) => {
+                    match method_call(recv) {
+                        Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::NeedlessMove(name, arg), false),
+                        Some(("chars", recv, _, _, _)) if let ExprKind::Closure(arg) = arg.kind
+                        && let body = cx.tcx.hir().body(arg.body)
+                        && let [param] = body.params => {
+                            string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
+                        }
+                        _ => {}
+                    }
                 }
                 ("arg", [arg]) => {
                     suspicious_command_arg_space::check(cx, recv, arg, span);
diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs
index 41986551da4..7016ad0a80f 100644
--- a/clippy_lints/src/methods/str_splitn.rs
+++ b/clippy_lints/src/methods/str_splitn.rs
@@ -236,7 +236,7 @@ fn indirect_usage<'tcx>(
             !matches!(
                 node,
                 Node::Expr(Expr {
-                    kind: ExprKind::Match(.., MatchSource::TryDesugar),
+                    kind: ExprKind::Match(.., MatchSource::TryDesugar(_)),
                     ..
                 })
             )
diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs
index a41d5a9ce8d..93f6025c71d 100644
--- a/clippy_lints/src/missing_inline.rs
+++ b/clippy_lints/src/missing_inline.rs
@@ -74,7 +74,6 @@ fn is_executable_or_proc_macro(cx: &LateContext<'_>) -> bool {
     use rustc_session::config::CrateType;
 
     cx.tcx
-        .sess
         .crate_types()
         .iter()
         .any(|t: &CrateType| matches!(t, CrateType::Executable | CrateType::ProcMacro))
diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs
index e2a7ba02a04..7b0f7eaf1f0 100644
--- a/clippy_lints/src/needless_question_mark.rs
+++ b/clippy_lints/src/needless_question_mark.rs
@@ -122,7 +122,7 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
         } else {
             return;
         };
-        if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &arg.kind;
+        if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar(_)) = &arg.kind;
         if let ExprKind::Call(called, [inner_expr]) = &inner_expr_with_q.kind;
         if let ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, ..)) = &called.kind;
         if expr.span.ctxt() == inner_expr.span.ctxt();
diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs
index 8009b00b4b9..bf031ac8454 100644
--- a/clippy_lints/src/ptr.rs
+++ b/clippy_lints/src/ptr.rs
@@ -278,7 +278,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
 
 fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
     // (fn_path, arg_indices) - `arg_indices` are the `arg` positions where null would cause U.B.
-    const INVALID_NULL_PTR_USAGE_TABLE: [(&[&str], &[usize]); 16] = [
+    const INVALID_NULL_PTR_USAGE_TABLE: [(&[&str], &[usize]); 13] = [
         (&paths::SLICE_FROM_RAW_PARTS, &[0]),
         (&paths::SLICE_FROM_RAW_PARTS_MUT, &[0]),
         (&paths::PTR_COPY, &[0, 1]),
@@ -291,20 +291,33 @@ fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         (&paths::PTR_SLICE_FROM_RAW_PARTS_MUT, &[0]),
         (&paths::PTR_SWAP, &[0, 1]),
         (&paths::PTR_SWAP_NONOVERLAPPING, &[0, 1]),
-        (&paths::PTR_WRITE, &[0]),
-        (&paths::PTR_WRITE_UNALIGNED, &[0]),
-        (&paths::PTR_WRITE_VOLATILE, &[0]),
         (&paths::PTR_WRITE_BYTES, &[0]),
     ];
+    let invalid_null_ptr_usage_table_diag_items: [(Option<DefId>, &[usize]); 3] = [
+        (cx.tcx.get_diagnostic_item(sym::ptr_write), &[0]),
+        (cx.tcx.get_diagnostic_item(sym::ptr_write_unaligned), &[0]),
+        (cx.tcx.get_diagnostic_item(sym::ptr_write_volatile), &[0]),
+    ];
 
     if_chain! {
         if let ExprKind::Call(fun, args) = expr.kind;
         if let ExprKind::Path(ref qpath) = fun.kind;
         if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
         let fun_def_path = cx.get_def_path(fun_def_id).into_iter().map(Symbol::to_ident_string).collect::<Vec<_>>();
-        if let Some(&(_, arg_indices)) = INVALID_NULL_PTR_USAGE_TABLE
+        if let Some(arg_indices) = INVALID_NULL_PTR_USAGE_TABLE
             .iter()
-            .find(|&&(fn_path, _)| fn_path == fun_def_path);
+            .find_map(|&(fn_path, indices)| if fn_path == fun_def_path { Some(indices) } else { None })
+            .or_else(|| {
+                invalid_null_ptr_usage_table_diag_items
+                    .iter()
+                    .find_map(|&(def_id, indices)| {
+                        if def_id == Some(fun_def_id) {
+                            Some(indices)
+                        } else {
+                            None
+                        }
+                    })
+            });
         then {
             for &arg_idx in arg_indices {
                 if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) {
diff --git a/clippy_lints/src/question_mark_used.rs b/clippy_lints/src/question_mark_used.rs
index ff66b8a0095..d0de33e3c4f 100644
--- a/clippy_lints/src/question_mark_used.rs
+++ b/clippy_lints/src/question_mark_used.rs
@@ -34,7 +34,7 @@ declare_lint_pass!(QuestionMarkUsed => [QUESTION_MARK_USED]);
 
 impl<'tcx> LateLintPass<'tcx> for QuestionMarkUsed {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if let ExprKind::Match(_, _, MatchSource::TryDesugar) = expr.kind {
+        if let ExprKind::Match(_, _, MatchSource::TryDesugar(_)) = expr.kind {
             if !span_is_local(expr.span) {
                 return;
             }
diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs
index 4e3efe97b8c..fc49b58e0a7 100644
--- a/clippy_lints/src/redundant_closure_call.rs
+++ b/clippy_lints/src/redundant_closure_call.rs
@@ -52,7 +52,7 @@ impl ReturnVisitor {
 
 impl<'tcx> Visitor<'tcx> for ReturnVisitor {
     fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
-        if let hir::ExprKind::Ret(_) | hir::ExprKind::Match(.., hir::MatchSource::TryDesugar) = ex.kind {
+        if let hir::ExprKind::Ret(_) | hir::ExprKind::Match(.., hir::MatchSource::TryDesugar(_)) = ex.kind {
             self.found_return = true;
         } else {
             hir_visit::walk_expr(self, ex);
diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs
index 351bacf5691..d6b9a49d2fe 100644
--- a/clippy_lints/src/returns.rs
+++ b/clippy_lints/src/returns.rs
@@ -164,7 +164,7 @@ impl<'tcx> LateLintPass<'tcx> for Return {
         if !in_external_macro(cx.sess(), stmt.span)
             && let StmtKind::Semi(expr) = stmt.kind
             && let ExprKind::Ret(Some(ret)) = expr.kind
-            && let ExprKind::Match(.., MatchSource::TryDesugar) = ret.kind
+            && let ExprKind::Match(.., MatchSource::TryDesugar(_)) = ret.kind
             // Ensure this is not the final stmt, otherwise removing it would cause a compile error
             && let OwnerNode::Item(item) = cx.tcx.hir().owner(cx.tcx.hir().get_parent_item(expr.hir_id))
             && let ItemKind::Fn(_, _, body) = item.kind
diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs
index dd120599c04..462b1aa8153 100644
--- a/clippy_lints/src/unit_types/unit_arg.rs
+++ b/clippy_lints/src/unit_types/unit_arg.rs
@@ -42,7 +42,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
             if cx.typeck_results().expr_ty(arg).is_unit() && !utils::is_unit_literal(arg) {
                 !matches!(
                     &arg.kind,
-                    ExprKind::Match(.., MatchSource::TryDesugar) | ExprKind::Path(..)
+                    ExprKind::Match(.., MatchSource::TryDesugar(_)) | ExprKind::Path(..)
                 )
             } else {
                 false
diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs
index 64dfe51b059..5ac4f0aa46c 100644
--- a/clippy_lints/src/useless_conversion.rs
+++ b/clippy_lints/src/useless_conversion.rs
@@ -116,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
         }
 
         match e.kind {
-            ExprKind::Match(_, arms, MatchSource::TryDesugar) => {
+            ExprKind::Match(_, arms, MatchSource::TryDesugar(_)) => {
                 let (ExprKind::Ret(Some(e)) | ExprKind::Break(_, Some(e))) = arms[0].body.kind else {
                     return;
                 };
diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml
index 3926b954ec8..1596bb77397 100644
--- a/clippy_utils/Cargo.toml
+++ b/clippy_utils/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_utils"
-version = "0.1.73"
+version = "0.1.74"
 edition = "2021"
 publish = false
 
diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs
index 60fab1ec41a..6be8b8bb916 100644
--- a/clippy_utils/src/check_proc_macro.rs
+++ b/clippy_utils/src/check_proc_macro.rs
@@ -149,7 +149,7 @@ fn expr_search_pat(tcx: TyCtxt<'_>, e: &Expr<'_>) -> (Pat, Pat) {
             (Pat::Str("for"), Pat::Str("}"))
         },
         ExprKind::Match(_, _, MatchSource::Normal) => (Pat::Str("match"), Pat::Str("}")),
-        ExprKind::Match(e, _, MatchSource::TryDesugar) => (expr_search_pat(tcx, e).0, Pat::Str("?")),
+        ExprKind::Match(e, _, MatchSource::TryDesugar(_)) => (expr_search_pat(tcx, e).0, Pat::Str("?")),
         ExprKind::Match(e, _, MatchSource::AwaitDesugar) | ExprKind::Yield(e, YieldSource::Await { .. }) => {
             (expr_search_pat(tcx, e).0, Pat::Str("await"))
         },
diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs
index fdc35cd4ddf..52214e733f1 100644
--- a/clippy_utils/src/hir_utils.rs
+++ b/clippy_utils/src/hir_utils.rs
@@ -5,6 +5,7 @@ use crate::tokenize_with_text;
 use rustc_ast::ast::InlineAsmTemplatePiece;
 use rustc_data_structures::fx::FxHasher;
 use rustc_hir::def::Res;
+use rustc_hir::MatchSource::TryDesugar;
 use rustc_hir::{
     ArrayLen, BinOpKind, BindingAnnotation, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg,
     GenericArgs, Guard, HirId, HirIdMap, InlineAsmOperand, Let, Lifetime, LifetimeName, Pat, PatField, PatKind, Path,
@@ -311,7 +312,7 @@ impl HirEqInterExpr<'_, '_, '_> {
                 lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name)
             },
             (&ExprKind::Match(le, la, ref ls), &ExprKind::Match(re, ra, ref rs)) => {
-                ls == rs
+                (ls == rs || (matches!((ls, rs), (TryDesugar(_), TryDesugar(_)))))
                     && self.eq_expr(le, re)
                     && over(la, ra, |l, r| {
                         self.eq_pat(l.pat, r.pat)
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index 171b7faf219..d53cafde45d 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -5,6 +5,7 @@
 #![feature(lint_reasons)]
 #![feature(never_type)]
 #![feature(rustc_private)]
+#![feature(assert_matches)]
 #![recursion_limit = "512"]
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
@@ -83,7 +84,7 @@ use rustc_ast::Attribute;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::unhash::UnhashMap;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
+use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalModDefId, LOCAL_CRATE};
 use rustc_hir::hir_id::{HirIdMap, HirIdSet};
 use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
@@ -1765,7 +1766,7 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc
 
     if let ExprKind::Match(_, arms, ref source) = expr.kind {
         // desugared from a `?` operator
-        if *source == MatchSource::TryDesugar {
+        if let MatchSource::TryDesugar(_) = *source {
             return Some(expr);
         }
 
@@ -2370,11 +2371,11 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
     false
 }
 
-static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = OnceLock::new();
+static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalModDefId, Vec<Symbol>>>> = OnceLock::new();
 
-fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
+fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
     let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
-    let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
+    let mut map: MutexGuard<'_, FxHashMap<LocalModDefId, Vec<Symbol>>> = cache.lock().unwrap();
     let value = map.entry(module);
     match value {
         Entry::Occupied(entry) => f(entry.get()),
diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs
index e72d063cfd9..a567c5cbdc9 100644
--- a/clippy_utils/src/paths.rs
+++ b/clippy_utils/src/paths.rs
@@ -87,10 +87,7 @@ pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"];
 pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"];
 pub const PTR_UNALIGNED_VOLATILE_LOAD: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_load"];
 pub const PTR_UNALIGNED_VOLATILE_STORE: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_store"];
-pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"];
 pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"];
-pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"];
-pub const PTR_WRITE_VOLATILE: [&str; 3] = ["core", "ptr", "write_volatile"];
 pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
 pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
 pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"];
diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs
index 4c695cb9b6e..43523faa236 100644
--- a/clippy_utils/src/qualify_min_const_fn.rs
+++ b/clippy_utils/src/qualify_min_const_fn.rs
@@ -291,8 +291,8 @@ fn check_terminator<'tcx>(
         | TerminatorKind::FalseUnwind { .. }
         | TerminatorKind::Goto { .. }
         | TerminatorKind::Return
-        | TerminatorKind::Resume
-        | TerminatorKind::Terminate
+        | TerminatorKind::UnwindResume
+        | TerminatorKind::UnwindTerminate
         | TerminatorKind::Unreachable => Ok(()),
         TerminatorKind::Drop { place, .. } => {
             if !is_ty_const_destruct(tcx, place.ty(&body.local_decls, tcx).ty, body) {
@@ -415,7 +415,7 @@ fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>
 
         if !matches!(
             impl_src,
-            ImplSource::Builtin(BuiltinImplSource::Misc, _) | ImplSource::Param(ty::BoundConstness::ConstIfConst, _)
+            ImplSource::Builtin(BuiltinImplSource::Misc, _) | ImplSource::Param(_)
         ) {
             return false;
         }
diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs
index a05f682aa8c..f0b4ede35fb 100644
--- a/clippy_utils/src/ty.rs
+++ b/clippy_utils/src/ty.rs
@@ -27,6 +27,7 @@ use rustc_target::abi::{Size, VariantIdx};
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
 use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
 use rustc_trait_selection::traits::{Obligation, ObligationCause};
+use std::assert_matches::debug_assert_matches;
 use std::iter;
 
 use crate::{match_def_path, path_res, paths};
@@ -259,7 +260,11 @@ pub fn implements_trait_with_env_from_iter<'tcx>(
             })),
     );
 
-    debug_assert_eq!(tcx.def_kind(trait_id), DefKind::Trait);
+    debug_assert_matches!(
+        tcx.def_kind(trait_id),
+        DefKind::Trait | DefKind::TraitAlias,
+        "`DefId` must belong to a trait or trait alias"
+    );
     #[cfg(debug_assertions)]
     assert_generic_args_match(tcx, trait_id, trait_ref.args);
 
diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs
index f83988a6e32..3b47a451345 100644
--- a/clippy_utils/src/visitors.rs
+++ b/clippy_utils/src/visitors.rs
@@ -161,7 +161,7 @@ pub fn for_each_expr_with_closures<'tcx, B, C: Continue>(
 /// returns `true` if expr contains match expr desugared from try
 fn contains_try(expr: &hir::Expr<'_>) -> bool {
     for_each_expr(expr, |e| {
-        if matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar)) {
+        if matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar(_))) {
             ControlFlow::Break(())
         } else {
             ControlFlow::Continue(())
diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml
index 3633ed31d73..1470da61fac 100644
--- a/declare_clippy_lint/Cargo.toml
+++ b/declare_clippy_lint/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "declare_clippy_lint"
-version = "0.1.73"
+version = "0.1.74"
 edition = "2021"
 publish = false
 
diff --git a/rust-toolchain b/rust-toolchain
index 8b3f819f0cd..19c09b58b98 100644
--- a/rust-toolchain
+++ b/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2023-08-10"
+channel = "nightly-2023-08-24"
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
diff --git a/tests/ui/crashes/ice-11337.rs b/tests/ui/crashes/ice-11337.rs
new file mode 100644
index 00000000000..0bed4035f6b
--- /dev/null
+++ b/tests/ui/crashes/ice-11337.rs
@@ -0,0 +1,9 @@
+#![feature(trait_alias)]
+
+trait Confusing<F> = Fn(i32) where F: Fn(u32);
+
+fn alias<T: Confusing<F>, F>(_: T, _: F) {}
+
+fn main() {
+    alias(|_| {}, |_| {});
+}
diff --git a/tests/ui/implied_bounds_in_impls.fixed b/tests/ui/implied_bounds_in_impls.fixed
new file mode 100644
index 00000000000..952c2b70619
--- /dev/null
+++ b/tests/ui/implied_bounds_in_impls.fixed
@@ -0,0 +1,68 @@
+#![warn(clippy::implied_bounds_in_impls)]
+#![allow(dead_code)]
+#![feature(return_position_impl_trait_in_trait)]
+
+use std::ops::{Deref, DerefMut};
+
+// Only one bound, nothing to lint.
+fn normal_deref<T>(x: T) -> impl Deref<Target = T> {
+    Box::new(x)
+}
+
+// Deref implied by DerefMut
+fn deref_derefmut<T>(x: T) -> impl DerefMut<Target = T> {
+    Box::new(x)
+}
+
+trait GenericTrait<T> {}
+trait GenericTrait2<V> {}
+// U is intentionally at a different "index" in GenericSubtrait than `T` is in GenericTrait,
+// so this can be a good test to make sure that the calculations are right (no off-by-one errors,
+// ...)
+trait GenericSubtrait<T, U, V>: GenericTrait<U> + GenericTrait2<V> {}
+
+impl GenericTrait<i32> for () {}
+impl GenericTrait<i64> for () {}
+impl<V> GenericTrait2<V> for () {}
+impl<V> GenericSubtrait<(), i32, V> for () {}
+impl<V> GenericSubtrait<(), i64, V> for () {}
+
+fn generics_implied<U, W>() -> impl GenericSubtrait<U, W, U>
+where
+    (): GenericSubtrait<U, W, U>,
+{
+}
+
+fn generics_implied_multi<V>() -> impl GenericSubtrait<(), i32, V> {}
+
+fn generics_implied_multi2<T, V>() -> impl GenericSubtrait<(), T, V>
+where
+    (): GenericSubtrait<(), T, V> + GenericTrait<T>,
+{
+}
+
+// i32 != i64, GenericSubtrait<_, i64, _> does not imply GenericTrait<i32>, don't lint
+fn generics_different() -> impl GenericTrait<i32> + GenericSubtrait<(), i64, ()> {}
+
+// i32 == i32, GenericSubtrait<_, i32, _> does imply GenericTrait<i32>, lint
+fn generics_same() -> impl GenericSubtrait<(), i32, ()> {}
+
+trait SomeTrait {
+    // Check that it works in trait declarations.
+    fn f() -> impl DerefMut<Target = u8>;
+}
+struct SomeStruct;
+impl SomeStruct {
+    // Check that it works in inherent impl blocks.
+    fn f() -> impl DerefMut<Target = u8> {
+        Box::new(123)
+    }
+}
+impl SomeTrait for SomeStruct {
+    // Check that it works in trait impls.
+    fn f() -> impl DerefMut<Target = u8> {
+        Box::new(42)
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/implied_bounds_in_impls.rs b/tests/ui/implied_bounds_in_impls.rs
new file mode 100644
index 00000000000..475f2621bbf
--- /dev/null
+++ b/tests/ui/implied_bounds_in_impls.rs
@@ -0,0 +1,68 @@
+#![warn(clippy::implied_bounds_in_impls)]
+#![allow(dead_code)]
+#![feature(return_position_impl_trait_in_trait)]
+
+use std::ops::{Deref, DerefMut};
+
+// Only one bound, nothing to lint.
+fn normal_deref<T>(x: T) -> impl Deref<Target = T> {
+    Box::new(x)
+}
+
+// Deref implied by DerefMut
+fn deref_derefmut<T>(x: T) -> impl Deref<Target = T> + DerefMut<Target = T> {
+    Box::new(x)
+}
+
+trait GenericTrait<T> {}
+trait GenericTrait2<V> {}
+// U is intentionally at a different "index" in GenericSubtrait than `T` is in GenericTrait,
+// so this can be a good test to make sure that the calculations are right (no off-by-one errors,
+// ...)
+trait GenericSubtrait<T, U, V>: GenericTrait<U> + GenericTrait2<V> {}
+
+impl GenericTrait<i32> for () {}
+impl GenericTrait<i64> for () {}
+impl<V> GenericTrait2<V> for () {}
+impl<V> GenericSubtrait<(), i32, V> for () {}
+impl<V> GenericSubtrait<(), i64, V> for () {}
+
+fn generics_implied<U, W>() -> impl GenericTrait<W> + GenericSubtrait<U, W, U>
+where
+    (): GenericSubtrait<U, W, U>,
+{
+}
+
+fn generics_implied_multi<V>() -> impl GenericTrait<i32> + GenericTrait2<V> + GenericSubtrait<(), i32, V> {}
+
+fn generics_implied_multi2<T, V>() -> impl GenericTrait<T> + GenericTrait2<V> + GenericSubtrait<(), T, V>
+where
+    (): GenericSubtrait<(), T, V> + GenericTrait<T>,
+{
+}
+
+// i32 != i64, GenericSubtrait<_, i64, _> does not imply GenericTrait<i32>, don't lint
+fn generics_different() -> impl GenericTrait<i32> + GenericSubtrait<(), i64, ()> {}
+
+// i32 == i32, GenericSubtrait<_, i32, _> does imply GenericTrait<i32>, lint
+fn generics_same() -> impl GenericTrait<i32> + GenericSubtrait<(), i32, ()> {}
+
+trait SomeTrait {
+    // Check that it works in trait declarations.
+    fn f() -> impl Deref + DerefMut<Target = u8>;
+}
+struct SomeStruct;
+impl SomeStruct {
+    // Check that it works in inherent impl blocks.
+    fn f() -> impl Deref + DerefMut<Target = u8> {
+        Box::new(123)
+    }
+}
+impl SomeTrait for SomeStruct {
+    // Check that it works in trait impls.
+    fn f() -> impl Deref + DerefMut<Target = u8> {
+        Box::new(42)
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/implied_bounds_in_impls.stderr b/tests/ui/implied_bounds_in_impls.stderr
new file mode 100644
index 00000000000..8dffc674444
--- /dev/null
+++ b/tests/ui/implied_bounds_in_impls.stderr
@@ -0,0 +1,123 @@
+error: this bound is already specified as the supertrait of `DerefMut<Target = T>`
+  --> $DIR/implied_bounds_in_impls.rs:13:36
+   |
+LL | fn deref_derefmut<T>(x: T) -> impl Deref<Target = T> + DerefMut<Target = T> {
+   |                                    ^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::implied-bounds-in-impls` implied by `-D warnings`
+help: try removing this bound
+   |
+LL - fn deref_derefmut<T>(x: T) -> impl Deref<Target = T> + DerefMut<Target = T> {
+LL + fn deref_derefmut<T>(x: T) -> impl DerefMut<Target = T> {
+   |
+
+error: this bound is already specified as the supertrait of `GenericSubtrait<U, W, U>`
+  --> $DIR/implied_bounds_in_impls.rs:30:37
+   |
+LL | fn generics_implied<U, W>() -> impl GenericTrait<W> + GenericSubtrait<U, W, U>
+   |                                     ^^^^^^^^^^^^^^^
+   |
+help: try removing this bound
+   |
+LL - fn generics_implied<U, W>() -> impl GenericTrait<W> + GenericSubtrait<U, W, U>
+LL + fn generics_implied<U, W>() -> impl GenericSubtrait<U, W, U>
+   |
+
+error: this bound is already specified as the supertrait of `GenericSubtrait<(), i32, V>`
+  --> $DIR/implied_bounds_in_impls.rs:36:40
+   |
+LL | fn generics_implied_multi<V>() -> impl GenericTrait<i32> + GenericTrait2<V> + GenericSubtrait<(), i32, V> {}
+   |                                        ^^^^^^^^^^^^^^^^^
+   |
+help: try removing this bound
+   |
+LL - fn generics_implied_multi<V>() -> impl GenericTrait<i32> + GenericTrait2<V> + GenericSubtrait<(), i32, V> {}
+LL + fn generics_implied_multi<V>() -> impl GenericTrait2<V> + GenericSubtrait<(), i32, V> {}
+   |
+
+error: this bound is already specified as the supertrait of `GenericSubtrait<(), i32, V>`
+  --> $DIR/implied_bounds_in_impls.rs:36:60
+   |
+LL | fn generics_implied_multi<V>() -> impl GenericTrait<i32> + GenericTrait2<V> + GenericSubtrait<(), i32, V> {}
+   |                                                            ^^^^^^^^^^^^^^^^
+   |
+help: try removing this bound
+   |
+LL - fn generics_implied_multi<V>() -> impl GenericTrait<i32> + GenericTrait2<V> + GenericSubtrait<(), i32, V> {}
+LL + fn generics_implied_multi<V>() -> impl GenericTrait<i32> + GenericSubtrait<(), i32, V> {}
+   |
+
+error: this bound is already specified as the supertrait of `GenericSubtrait<(), T, V>`
+  --> $DIR/implied_bounds_in_impls.rs:38:44
+   |
+LL | fn generics_implied_multi2<T, V>() -> impl GenericTrait<T> + GenericTrait2<V> + GenericSubtrait<(), T, V>
+   |                                            ^^^^^^^^^^^^^^^
+   |
+help: try removing this bound
+   |
+LL - fn generics_implied_multi2<T, V>() -> impl GenericTrait<T> + GenericTrait2<V> + GenericSubtrait<(), T, V>
+LL + fn generics_implied_multi2<T, V>() -> impl GenericTrait2<V> + GenericSubtrait<(), T, V>
+   |
+
+error: this bound is already specified as the supertrait of `GenericSubtrait<(), T, V>`
+  --> $DIR/implied_bounds_in_impls.rs:38:62
+   |
+LL | fn generics_implied_multi2<T, V>() -> impl GenericTrait<T> + GenericTrait2<V> + GenericSubtrait<(), T, V>
+   |                                                              ^^^^^^^^^^^^^^^^
+   |
+help: try removing this bound
+   |
+LL - fn generics_implied_multi2<T, V>() -> impl GenericTrait<T> + GenericTrait2<V> + GenericSubtrait<(), T, V>
+LL + fn generics_implied_multi2<T, V>() -> impl GenericTrait<T> + GenericSubtrait<(), T, V>
+   |
+
+error: this bound is already specified as the supertrait of `GenericSubtrait<(), i32, ()>`
+  --> $DIR/implied_bounds_in_impls.rs:48:28
+   |
+LL | fn generics_same() -> impl GenericTrait<i32> + GenericSubtrait<(), i32, ()> {}
+   |                            ^^^^^^^^^^^^^^^^^
+   |
+help: try removing this bound
+   |
+LL - fn generics_same() -> impl GenericTrait<i32> + GenericSubtrait<(), i32, ()> {}
+LL + fn generics_same() -> impl GenericSubtrait<(), i32, ()> {}
+   |
+
+error: this bound is already specified as the supertrait of `DerefMut<Target = u8>`
+  --> $DIR/implied_bounds_in_impls.rs:52:20
+   |
+LL |     fn f() -> impl Deref + DerefMut<Target = u8>;
+   |                    ^^^^^
+   |
+help: try removing this bound
+   |
+LL -     fn f() -> impl Deref + DerefMut<Target = u8>;
+LL +     fn f() -> impl DerefMut<Target = u8>;
+   |
+
+error: this bound is already specified as the supertrait of `DerefMut<Target = u8>`
+  --> $DIR/implied_bounds_in_impls.rs:57:20
+   |
+LL |     fn f() -> impl Deref + DerefMut<Target = u8> {
+   |                    ^^^^^
+   |
+help: try removing this bound
+   |
+LL -     fn f() -> impl Deref + DerefMut<Target = u8> {
+LL +     fn f() -> impl DerefMut<Target = u8> {
+   |
+
+error: this bound is already specified as the supertrait of `DerefMut<Target = u8>`
+  --> $DIR/implied_bounds_in_impls.rs:63:20
+   |
+LL |     fn f() -> impl Deref + DerefMut<Target = u8> {
+   |                    ^^^^^
+   |
+help: try removing this bound
+   |
+LL -     fn f() -> impl Deref + DerefMut<Target = u8> {
+LL +     fn f() -> impl DerefMut<Target = u8> {
+   |
+
+error: aborting due to 10 previous errors
+
diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed
index 9dd046fec1f..7d8a584b022 100644
--- a/tests/ui/iter_overeager_cloned.fixed
+++ b/tests/ui/iter_overeager_cloned.fixed
@@ -63,11 +63,9 @@ fn main() {
 
     let _ = vec.iter().for_each(|x| assert!(!x.is_empty()));
 
-    // Not implemented yet
-    let _ = vec.iter().cloned().all(|x| x.len() == 1);
+    let _ = vec.iter().all(|x| x.len() == 1);
 
-    // Not implemented yet
-    let _ = vec.iter().cloned().any(|x| x.len() == 1);
+    let _ = vec.iter().any(|x| x.len() == 1);
 
     // Should probably stay as it is.
     let _ = [0, 1, 2, 3, 4].iter().cloned().take(10);
diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs
index 3cab3669554..58c374ab8cd 100644
--- a/tests/ui/iter_overeager_cloned.rs
+++ b/tests/ui/iter_overeager_cloned.rs
@@ -64,10 +64,8 @@ fn main() {
 
     let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty()));
 
-    // Not implemented yet
     let _ = vec.iter().cloned().all(|x| x.len() == 1);
 
-    // Not implemented yet
     let _ = vec.iter().cloned().any(|x| x.len() == 1);
 
     // Should probably stay as it is.
diff --git a/tests/ui/iter_overeager_cloned.stderr b/tests/ui/iter_overeager_cloned.stderr
index 4417a40a63b..1f4c9040f77 100644
--- a/tests/ui/iter_overeager_cloned.stderr
+++ b/tests/ui/iter_overeager_cloned.stderr
@@ -146,5 +146,21 @@ LL |     let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty()));
    |                       |
    |                       help: try: `.for_each(|x| assert!(!x.is_empty()))`
 
-error: aborting due to 17 previous errors
+error: unneeded cloning of iterator items
+  --> $DIR/iter_overeager_cloned.rs:67:13
+   |
+LL |     let _ = vec.iter().cloned().all(|x| x.len() == 1);
+   |             ^^^^^^^^^^-------------------------------
+   |                       |
+   |                       help: try: `.all(|x| x.len() == 1)`
+
+error: unneeded cloning of iterator items
+  --> $DIR/iter_overeager_cloned.rs:69:13
+   |
+LL |     let _ = vec.iter().cloned().any(|x| x.len() == 1);
+   |             ^^^^^^^^^^-------------------------------
+   |                       |
+   |                       help: try: `.any(|x| x.len() == 1)`
+
+error: aborting due to 19 previous errors