about summary refs log tree commit diff
path: root/src/tools/clippy/clippy_lints
diff options
context:
space:
mode:
authorPhilipp Krones <hello@philkrones.com>2023-04-11 15:31:08 +0200
committerPhilipp Krones <hello@philkrones.com>2023-04-11 15:31:08 +0200
commit0ca8ae5edd730d3c17b3f839dcff9aa906aa8c19 (patch)
treed4de99e1486055040888a6096e95a87e9c44f6d0 /src/tools/clippy/clippy_lints
parentdfe024e1041d1cba1d3191024de3b7128c6734e6 (diff)
parent83e42a2337dadac915c956d125f1d69132f36425 (diff)
downloadrust-0ca8ae5edd730d3c17b3f839dcff9aa906aa8c19.tar.gz
rust-0ca8ae5edd730d3c17b3f839dcff9aa906aa8c19.zip
Merge commit '83e42a2337dadac915c956d125f1d69132f36425' into clippyup
Diffstat (limited to 'src/tools/clippy/clippy_lints')
-rw-r--r--src/tools/clippy/clippy_lints/Cargo.toml1
-rw-r--r--src/tools/clippy/clippy_lints/src/booleans.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs42
-rw-r--r--src/tools/clippy/clippy_lints/src/collection_is_never_read.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/explicit_write.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs177
-rw-r--r--src/tools/clippy/clippy_lints/src/format.rs91
-rw-r--r--src/tools/clippy/clippy_lints/src/format_args.rs224
-rw-r--r--src/tools/clippy/clippy_lints/src/format_impl.rs60
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/must_use.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/items_after_statements.rs31
-rw-r--r--src/tools/clippy/clippy_lints/src/large_futures.rs87
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs36
-rw-r--r--src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs100
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs93
-rw-r--r--src/tools/clippy/clippy_lints/src/mem_replace.rs71
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs53
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs27
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs40
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_async_block.rs145
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/returns.rs32
-rw-r--r--src/tools/clippy/clippy_lints/src/single_component_path_imports.rs59
-rw-r--r--src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs94
-rw-r--r--src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs71
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs120
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/use_self.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/conf.rs36
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs79
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs14
37 files changed, 1468 insertions, 513 deletions
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
index 0b3846c1316..18e8bf77225 100644
--- a/src/tools/clippy/clippy_lints/Cargo.toml
+++ b/src/tools/clippy/clippy_lints/Cargo.toml
@@ -9,6 +9,7 @@ keywords = ["clippy", "lint", "plugin"]
 edition = "2021"
 
 [dependencies]
+arrayvec = { version = "0.7", default-features = false }
 cargo_metadata = "0.15.3"
 clippy_utils = { path = "../clippy_utils" }
 declare_clippy_lint = { path = "../declare_clippy_lint" }
diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs
index 29fde9336c0..455f0df7cd0 100644
--- a/src/tools/clippy/clippy_lints/src/booleans.rs
+++ b/src/tools/clippy/clippy_lints/src/booleans.rs
@@ -7,7 +7,7 @@ use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, Level};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::def_id::LocalDefId;
 use rustc_span::source_map::Span;
@@ -430,23 +430,25 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
                 }
             }
             let nonminimal_bool_lint = |suggestions: Vec<_>| {
-                span_lint_hir_and_then(
-                    self.cx,
-                    NONMINIMAL_BOOL,
-                    e.hir_id,
-                    e.span,
-                    "this boolean expression can be simplified",
-                    |diag| {
-                        diag.span_suggestions(
-                            e.span,
-                            "try",
-                            suggestions.into_iter(),
-                            // nonminimal_bool can produce minimal but
-                            // not human readable expressions (#3141)
-                            Applicability::Unspecified,
-                        );
-                    },
-                );
+                if self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, e.hir_id).0 != Level::Allow {
+                    span_lint_hir_and_then(
+                        self.cx,
+                        NONMINIMAL_BOOL,
+                        e.hir_id,
+                        e.span,
+                        "this boolean expression can be simplified",
+                        |diag| {
+                            diag.span_suggestions(
+                                e.span,
+                                "try",
+                                suggestions.into_iter(),
+                                // nonminimal_bool can produce minimal but
+                                // not human readable expressions (#3141)
+                                Applicability::Unspecified,
+                            );
+                        },
+                    );
+                }
             };
             if improvements.is_empty() {
                 let mut visitor = NotSimplificationVisitor { cx: self.cx };
@@ -498,6 +500,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> {
         if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind &&
             !inner.span.from_expansion() &&
             let Some(suggestion) = simplify_not(self.cx, inner)
+			&& self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow
         {
             span_lint_and_sugg(
                 self.cx,
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
index 823970e35ab..95c2ecbf791 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
@@ -2,8 +2,9 @@ use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use clippy_utils::expr_or_init;
 use clippy_utils::source::snippet;
+use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize};
-use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_errors::{Applicability, Diagnostic, SuggestionStyle};
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::LateContext;
@@ -163,19 +164,34 @@ pub(super) fn check(
         _ => return,
     };
 
-    let name_of_cast_from = snippet(cx, cast_expr.span, "..");
-    let cast_to_snip = snippet(cx, cast_to_span, "..");
-    let suggestion = format!("{cast_to_snip}::try_from({name_of_cast_from})");
-
     span_lint_and_then(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg, |diag| {
         diag.help("if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...");
-        diag.span_suggestion_with_style(
-            expr.span,
-            "... or use `try_from` and handle the error accordingly",
-            suggestion,
-            Applicability::Unspecified,
-            // always show the suggestion in a separate line
-            SuggestionStyle::ShowAlways,
-        );
+        if !cast_from.is_floating_point() {
+            offer_suggestion(cx, expr, cast_expr, cast_to_span, diag);
+        }
     });
 }
+
+fn offer_suggestion(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    cast_expr: &Expr<'_>,
+    cast_to_span: Span,
+    diag: &mut Diagnostic,
+) {
+    let cast_to_snip = snippet(cx, cast_to_span, "..");
+    let suggestion = if cast_to_snip == "_" {
+        format!("{}.try_into()", Sugg::hir(cx, cast_expr, "..").maybe_par())
+    } else {
+        format!("{cast_to_snip}::try_from({})", Sugg::hir(cx, cast_expr, ".."))
+    };
+
+    diag.span_suggestion_with_style(
+        expr.span,
+        "... or use `try_from` and handle the error accordingly",
+        suggestion,
+        Applicability::Unspecified,
+        // always show the suggestion in a separate line
+        SuggestionStyle::ShowAlways,
+    );
+}
diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
index 10f2bef268a..5e2eb5789f6 100644
--- a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
+++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
@@ -1,9 +1,9 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
 use clippy_utils::visitors::for_each_expr_with_closures;
 use clippy_utils::{get_enclosing_block, get_parent_node, path_to_local_id};
 use core::ops::ControlFlow;
-use rustc_hir::{Block, ExprKind, HirId, Local, Node, PatKind};
+use rustc_hir::{Block, ExprKind, HirId, LangItem, Local, Node, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::symbol::sym;
@@ -44,7 +44,8 @@ declare_clippy_lint! {
 }
 declare_lint_pass!(CollectionIsNeverRead => [COLLECTION_IS_NEVER_READ]);
 
-static COLLECTIONS: [Symbol; 10] = [
+// Add `String` here when it is added to diagnostic items
+static COLLECTIONS: [Symbol; 9] = [
     sym::BTreeMap,
     sym::BTreeSet,
     sym::BinaryHeap,
@@ -52,7 +53,6 @@ static COLLECTIONS: [Symbol; 10] = [
     sym::HashSet,
     sym::LinkedList,
     sym::Option,
-    sym::String,
     sym::Vec,
     sym::VecDeque,
 ];
@@ -60,8 +60,7 @@ static COLLECTIONS: [Symbol; 10] = [
 impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
         // Look for local variables whose type is a container. Search surrounding bock for read access.
-        let ty = cx.typeck_results().pat_ty(local.pat);
-        if COLLECTIONS.iter().any(|&sym| is_type_diagnostic_item(cx, ty, sym))
+        if match_acceptable_type(cx, local, &COLLECTIONS)
             && let PatKind::Binding(_, local_id, _, _) = local.pat.kind
             && let Some(enclosing_block) = get_enclosing_block(cx, local.hir_id)
             && has_no_read_access(cx, local_id, enclosing_block)
@@ -71,6 +70,13 @@ impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
     }
 }
 
+fn match_acceptable_type(cx: &LateContext<'_>, local: &Local<'_>, collections: &[rustc_span::Symbol]) -> bool {
+    let ty = cx.typeck_results().pat_ty(local.pat);
+    collections.iter().any(|&sym| is_type_diagnostic_item(cx, ty, sym))
+    // String type is a lang item but not a diagnostic item for now so we need a separate check
+        || is_type_lang_item(cx, ty, LangItem::String)
+}
+
 fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Block<'tcx>) -> bool {
     let mut has_access = false;
     let mut has_read_access = false;
@@ -95,9 +101,9 @@ fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Bloc
             return ControlFlow::Continue(());
         }
 
-        // Method call on `id` in a statement ignores any return value, so it's not a read access:
+        // Look for method call with receiver `id`. It might be a non-read access:
         //
-        // id.foo(...); // Not reading `id`.
+        // id.foo(args)
         //
         // Only assuming this for "official" methods defined on the type. For methods defined in extension
         // traits (identified as local, based on the orphan rule), pessimistically assume that they might
@@ -105,11 +111,24 @@ fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Bloc
         if let Some(Node::Expr(parent)) = get_parent_node(cx.tcx, expr.hir_id)
             && let ExprKind::MethodCall(_, receiver, _, _) = parent.kind
             && path_to_local_id(receiver, id)
-            && let Some(Node::Stmt(..)) = get_parent_node(cx.tcx, parent.hir_id)
             && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id)
             && !method_def_id.is_local()
         {
-            return ControlFlow::Continue(());
+            // The method call is a statement, so the return value is not used. That's not a read access:
+            //
+            // id.foo(args);
+            if let Some(Node::Stmt(..)) = get_parent_node(cx.tcx, parent.hir_id) {
+                return ControlFlow::Continue(());
+            }
+
+            // The method call is not a statement, so its return value is used somehow but its type is the
+            // unit type, so this is not a real read access. Examples:
+            //
+            // let y = x.clear();
+            // println!("{:?}", x.clear());
+            if cx.typeck_results().expr_ty(parent).is_unit() {
+                return ControlFlow::Continue(());
+            }
         }
 
         // Any other access to `id` is a read access. Stop searching.
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index 8ca91301472..f24dab62780 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -218,6 +218,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO,
     crate::large_const_arrays::LARGE_CONST_ARRAYS_INFO,
     crate::large_enum_variant::LARGE_ENUM_VARIANT_INFO,
+    crate::large_futures::LARGE_FUTURES_INFO,
     crate::large_include_file::LARGE_INCLUDE_FILE_INFO,
     crate::large_stack_arrays::LARGE_STACK_ARRAYS_INFO,
     crate::len_zero::COMPARISON_TO_EMPTY_INFO,
@@ -231,6 +232,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::let_with_type_underscore::LET_WITH_TYPE_UNDERSCORE_INFO,
     crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO,
     crate::lifetimes::NEEDLESS_LIFETIMES_INFO,
+    crate::lines_filter_map_ok::LINES_FILTER_MAP_OK_INFO,
     crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO,
     crate::literal_representation::INCONSISTENT_DIGIT_GROUPING_INFO,
     crate::literal_representation::LARGE_DIGIT_GROUPS_INFO,
@@ -267,6 +269,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO,
     crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO,
     crate::manual_retain::MANUAL_RETAIN_INFO,
+    crate::manual_slice_size_calculation::MANUAL_SLICE_SIZE_CALCULATION_INFO,
     crate::manual_string_new::MANUAL_STRING_NEW_INFO,
     crate::manual_strip::MANUAL_STRIP_INFO,
     crate::map_unit_fn::OPTION_MAP_UNIT_FN_INFO,
@@ -307,6 +310,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS_INFO,
     crate::methods::CHARS_LAST_CMP_INFO,
     crate::methods::CHARS_NEXT_CMP_INFO,
+    crate::methods::CLEAR_WITH_DRAIN_INFO,
     crate::methods::CLONED_INSTEAD_OF_COPIED_INFO,
     crate::methods::CLONE_DOUBLE_REF_INFO,
     crate::methods::CLONE_ON_COPY_INFO,
@@ -565,6 +569,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::strings::STR_TO_STRING_INFO,
     crate::strings::TRIM_SPLIT_WHITESPACE_INFO,
     crate::strlen_on_c_strings::STRLEN_ON_C_STRINGS_INFO,
+    crate::suspicious_doc_comments::SUSPICIOUS_DOC_COMMENTS_INFO,
     crate::suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS_INFO,
     crate::suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL_INFO,
     crate::suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL_INFO,
@@ -574,6 +579,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::swap_ptr_to_ref::SWAP_PTR_TO_REF_INFO,
     crate::tabs_in_doc_comments::TABS_IN_DOC_COMMENTS_INFO,
     crate::temporary_assignment::TEMPORARY_ASSIGNMENT_INFO,
+    crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO,
     crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO,
     crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO,
     crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO,
@@ -616,6 +622,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::unit_types::UNIT_CMP_INFO,
     crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO,
     crate::unnamed_address::VTABLE_ADDRESS_COMPARISONS_INFO,
+    crate::unnecessary_box_returns::UNNECESSARY_BOX_RETURNS_INFO,
     crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO,
     crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO,
     crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO,
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
index 084190f0013..c9fad98e437 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
@@ -32,7 +32,7 @@ declare_clippy_lint! {
     /// ### Example
     /// ```rust
     /// // Assuming that `clippy.toml` contains the following line:
-    /// // allowed-locales = ["Latin", "Cyrillic"]
+    /// // allowed-scripts = ["Latin", "Cyrillic"]
     /// let counter = 10; // OK, latin is allowed.
     /// let счётчик = 10; // OK, cyrillic is allowed.
     /// let zähler = 10; // OK, it's still latin.
diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs
index c0ea6f338a2..315df6c714f 100644
--- a/src/tools/clippy/clippy_lints/src/explicit_write.rs
+++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::macros::FormatArgsExpn;
+use clippy_utils::macros::{find_format_args, format_args_inputs_span};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::{is_expn_of, match_function_call, paths};
 use if_chain::if_chain;
@@ -8,7 +8,7 @@ use rustc_hir::def::Res;
 use rustc_hir::{BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
+use rustc_span::{sym, ExpnId};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -43,23 +43,22 @@ declare_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]);
 
 impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if_chain! {
-            // match call to unwrap
-            if let ExprKind::MethodCall(unwrap_fun, write_call, [], _) = expr.kind;
-            if unwrap_fun.ident.name == sym::unwrap;
+        // match call to unwrap
+        if let ExprKind::MethodCall(unwrap_fun, write_call, [], _) = expr.kind
+            && unwrap_fun.ident.name == sym::unwrap
             // match call to write_fmt
-            if let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = look_in_block(cx, &write_call.kind);
-            if write_fun.ident.name == sym!(write_fmt);
+            && let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = look_in_block(cx, &write_call.kind)
+            && write_fun.ident.name == sym!(write_fmt)
             // match calls to std::io::stdout() / std::io::stderr ()
-            if let Some(dest_name) = if match_function_call(cx, write_recv, &paths::STDOUT).is_some() {
+            && let Some(dest_name) = if match_function_call(cx, write_recv, &paths::STDOUT).is_some() {
                 Some("stdout")
             } else if match_function_call(cx, write_recv, &paths::STDERR).is_some() {
                 Some("stderr")
             } else {
                 None
-            };
-            if let Some(format_args) = FormatArgsExpn::parse(cx, write_arg);
-            then {
+            }
+        {
+            find_format_args(cx, write_arg, ExpnId::root(), |format_args| {
                 let calling_macro =
                     // ordering is important here, since `writeln!` uses `write!` internally
                     if is_expn_of(write_call.span, "writeln").is_some() {
@@ -92,7 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
                 let mut applicability = Applicability::MachineApplicable;
                 let inputs_snippet = snippet_with_applicability(
                     cx,
-                    format_args.inputs_span(),
+                    format_args_inputs_span(format_args),
                     "..",
                     &mut applicability,
                 );
@@ -104,8 +103,8 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
                     "try this",
                     format!("{prefix}{sugg_mac}!({inputs_snippet})"),
                     applicability,
-                )
-            }
+                );
+            });
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
index 20565e1d232..eeb4de8b58f 100644
--- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
+++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
@@ -1,10 +1,10 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
 use clippy_utils::trait_ref_of_method;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::MultiSpan;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_impl_item, walk_item, walk_param_bound, walk_ty, Visitor};
 use rustc_hir::{
-    BodyId, ExprKind, GenericBound, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind,
+    BodyId, ExprKind, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind,
     PredicateOrigin, Ty, TyKind, WherePredicate,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -53,13 +53,19 @@ impl ExtraUnusedTypeParameters {
         }
     }
 
-    /// Don't lint external macros or functions with empty bodies. Also, don't lint public items if
-    /// the `avoid_breaking_exported_api` config option is set.
-    fn check_false_positive(&self, cx: &LateContext<'_>, span: Span, def_id: LocalDefId, body_id: BodyId) -> bool {
+    /// Don't lint external macros or functions with empty bodies. Also, don't lint exported items
+    /// if the `avoid_breaking_exported_api` config option is set.
+    fn is_empty_exported_or_macro(
+        &self,
+        cx: &LateContext<'_>,
+        span: Span,
+        def_id: LocalDefId,
+        body_id: BodyId,
+    ) -> bool {
         let body = cx.tcx.hir().body(body_id).value;
         let fn_empty = matches!(&body.kind, ExprKind::Block(blk, None) if blk.stmts.is_empty() && blk.expr.is_none());
         let is_exported = cx.effective_visibilities.is_exported(def_id);
-        in_external_macro(cx.sess(), span) || (self.avoid_breaking_exported_api && is_exported) || fn_empty
+        in_external_macro(cx.sess(), span) || fn_empty || (is_exported && self.avoid_breaking_exported_api)
     }
 }
 
@@ -69,85 +75,129 @@ impl_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]);
 /// trait bounds those parameters have.
 struct TypeWalker<'cx, 'tcx> {
     cx: &'cx LateContext<'tcx>,
-    /// Collection of all the function's type parameters.
+    /// Collection of the function's type parameters. Once the function has been walked, this will
+    /// contain only unused type parameters.
     ty_params: FxHashMap<DefId, Span>,
-    /// Collection of any (inline) trait bounds corresponding to each type parameter.
-    bounds: FxHashMap<DefId, Span>,
+    /// Collection of any inline trait bounds corresponding to each type parameter.
+    inline_bounds: FxHashMap<DefId, Span>,
+    /// Collection of any type parameters with trait bounds that appear in a where clause.
+    where_bounds: FxHashSet<DefId>,
     /// The entire `Generics` object of the function, useful for querying purposes.
     generics: &'tcx Generics<'tcx>,
-    /// The value of this will remain `true` if *every* parameter:
-    ///   1. Is a type parameter, and
-    ///   2. Goes unused in the function.
-    /// Otherwise, if any type parameters end up being used, or if any lifetime or const-generic
-    /// parameters are present, this will be set to `false`.
-    all_params_unused: bool,
 }
 
 impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
     fn new(cx: &'cx LateContext<'tcx>, generics: &'tcx Generics<'tcx>) -> Self {
-        let mut all_params_unused = true;
         let ty_params = generics
             .params
             .iter()
-            .filter_map(|param| {
-                if let GenericParamKind::Type { synthetic, .. } = param.kind {
-                    (!synthetic).then_some((param.def_id.into(), param.span))
-                } else {
-                    if !param.is_elided_lifetime() {
-                        all_params_unused = false;
-                    }
-                    None
-                }
+            .filter_map(|param| match param.kind {
+                GenericParamKind::Type { synthetic, .. } if !synthetic => Some((param.def_id.into(), param.span)),
+                _ => None,
             })
             .collect();
 
         Self {
             cx,
             ty_params,
-            bounds: FxHashMap::default(),
+            inline_bounds: FxHashMap::default(),
+            where_bounds: FxHashSet::default(),
             generics,
-            all_params_unused,
         }
     }
 
-    fn mark_param_used(&mut self, def_id: DefId) {
-        if self.ty_params.remove(&def_id).is_some() {
-            self.all_params_unused = false;
-        }
+    fn get_bound_span(&self, param: &'tcx GenericParam<'tcx>) -> Span {
+        self.inline_bounds
+            .get(&param.def_id.to_def_id())
+            .map_or(param.span, |bound_span| param.span.with_hi(bound_span.hi()))
+    }
+
+    fn emit_help(&self, spans: Vec<Span>, msg: &str, help: &'static str) {
+        span_lint_and_help(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, spans, msg, None, help);
+    }
+
+    fn emit_sugg(&self, spans: Vec<Span>, msg: &str, help: &'static str) {
+        let suggestions: Vec<(Span, String)> = spans.iter().copied().zip(std::iter::repeat(String::new())).collect();
+        span_lint_and_then(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, spans, msg, |diag| {
+            diag.multipart_suggestion(help, suggestions, Applicability::MachineApplicable);
+        });
     }
 
     fn emit_lint(&self) {
-        let (msg, help) = match self.ty_params.len() {
+        let explicit_params = self
+            .generics
+            .params
+            .iter()
+            .filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait())
+            .collect::<Vec<_>>();
+
+        let extra_params = explicit_params
+            .iter()
+            .enumerate()
+            .filter(|(_, param)| self.ty_params.contains_key(&param.def_id.to_def_id()))
+            .collect::<Vec<_>>();
+
+        let (msg, help) = match extra_params.len() {
             0 => return,
             1 => (
-                "type parameter goes unused in function definition",
+                format!(
+                    "type parameter `{}` goes unused in function definition",
+                    extra_params[0].1.name.ident()
+                ),
                 "consider removing the parameter",
             ),
             _ => (
-                "type parameters go unused in function definition",
+                format!(
+                    "type parameters go unused in function definition: {}",
+                    extra_params
+                        .iter()
+                        .map(|(_, param)| param.name.ident().to_string())
+                        .collect::<Vec<_>>()
+                        .join(", ")
+                ),
                 "consider removing the parameters",
             ),
         };
 
-        let source_map = self.cx.sess().source_map();
-        let span = if self.all_params_unused {
-            self.generics.span.into() // Remove the entire list of generics
+        // If any parameters are bounded in where clauses, don't try to form a suggestion.
+        // Otherwise, the leftover where bound would produce code that wouldn't compile.
+        if extra_params
+            .iter()
+            .any(|(_, param)| self.where_bounds.contains(&param.def_id.to_def_id()))
+        {
+            let spans = extra_params
+                .iter()
+                .map(|(_, param)| self.get_bound_span(param))
+                .collect::<Vec<_>>();
+            self.emit_help(spans, &msg, help);
         } else {
-            MultiSpan::from_spans(
-                self.ty_params
+            let spans = if explicit_params.len() == extra_params.len() {
+                vec![self.generics.span] // Remove the entire list of generics
+            } else {
+                let mut end: Option<LocalDefId> = None;
+                extra_params
                     .iter()
-                    .map(|(def_id, &span)| {
-                        // Extend the span past any trait bounds, and include the comma at the end.
-                        let span_to_extend = self.bounds.get(def_id).copied().map_or(span, Span::shrink_to_hi);
-                        let comma_range = source_map.span_extend_to_next_char(span_to_extend, '>', false);
-                        let comma_span = source_map.span_through_char(comma_range, ',');
-                        span.with_hi(comma_span.hi())
+                    .rev()
+                    .map(|(idx, param)| {
+                        if let Some(next) = explicit_params.get(idx + 1) && end != Some(next.def_id) {
+                        // Extend the current span forward, up until the next param in the list.
+                        param.span.until(next.span)
+                    } else {
+                        // Extend the current span back to include the comma following the previous
+                        // param. If the span of the next param in the list has already been
+                        // extended, we continue the chain. This is why we're iterating in reverse.
+                        end = Some(param.def_id);
+
+                        // idx will never be 0, else we'd be removing the entire list of generics
+                        let prev = explicit_params[idx - 1];
+                        let prev_span = self.get_bound_span(prev);
+                        self.get_bound_span(param).with_lo(prev_span.hi())
+                    }
                     })
-                    .collect(),
-            )
+                    .collect()
+            };
+            self.emit_sugg(spans, &msg, help);
         };
-
-        span_lint_and_help(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, span, msg, None, help);
     }
 }
 
@@ -162,7 +212,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
 
     fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) {
         if let Some((def_id, _)) = t.peel_refs().as_generic_param() {
-            self.mark_param_used(def_id);
+            self.ty_params.remove(&def_id);
         } else if let TyKind::OpaqueDef(id, _, _) = t.kind {
             // Explicitly walk OpaqueDef. Normally `walk_ty` would do the job, but it calls
             // `visit_nested_item`, which checks that `Self::NestedFilter::INTER` is set. We're
@@ -176,9 +226,18 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
 
     fn visit_where_predicate(&mut self, predicate: &'tcx WherePredicate<'tcx>) {
         if let WherePredicate::BoundPredicate(predicate) = predicate {
-            // Collect spans for any bounds on type parameters. We only keep bounds that appear in
-            // the list of generics (not in a where-clause).
+            // Collect spans for any bounds on type parameters.
             if let Some((def_id, _)) = predicate.bounded_ty.peel_refs().as_generic_param() {
+                match predicate.origin {
+                    PredicateOrigin::GenericParam => {
+                        self.inline_bounds.insert(def_id, predicate.span);
+                    },
+                    PredicateOrigin::WhereClause => {
+                        self.where_bounds.insert(def_id);
+                    },
+                    PredicateOrigin::ImplTrait => (),
+                }
+
                 // If the bound contains non-public traits, err on the safe side and don't lint the
                 // corresponding parameter.
                 if !predicate
@@ -187,12 +246,10 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
                     .filter_map(bound_to_trait_def_id)
                     .all(|id| self.cx.effective_visibilities.is_exported(id))
                 {
-                    self.mark_param_used(def_id);
-                } else if let PredicateOrigin::GenericParam = predicate.origin {
-                    self.bounds.insert(def_id, predicate.span);
+                    self.ty_params.remove(&def_id);
                 }
             }
-            // Only walk the right-hand side of where-bounds
+            // Only walk the right-hand side of where bounds
             for bound in predicate.bounds {
                 walk_param_bound(self, bound);
             }
@@ -207,7 +264,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
 impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
         if let ItemKind::Fn(_, generics, body_id) = item.kind
-            && !self.check_false_positive(cx, item.span, item.owner_id.def_id, body_id)
+            && !self.is_empty_exported_or_macro(cx, item.span, item.owner_id.def_id, body_id)
         {
             let mut walker = TypeWalker::new(cx, generics);
             walk_item(&mut walker, item);
@@ -219,7 +276,7 @@ impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters {
         // Only lint on inherent methods, not trait methods.
         if let ImplItemKind::Fn(.., body_id) = item.kind
             && trait_ref_of_method(cx, item.owner_id.def_id).is_none()
-            && !self.check_false_positive(cx, item.span, item.owner_id.def_id, body_id)
+            && !self.is_empty_exported_or_macro(cx, item.span, item.owner_id.def_id, body_id)
         {
             let mut walker = TypeWalker::new(cx, item.generics);
             walk_impl_item(&mut walker, item);
diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs
index 8040938c626..d34d6e9279e 100644
--- a/src/tools/clippy/clippy_lints/src/format.rs
+++ b/src/tools/clippy/clippy_lints/src/format.rs
@@ -1,14 +1,13 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
-use clippy_utils::source::snippet_with_context;
+use clippy_utils::macros::{find_format_arg_expr, find_format_args, root_macro_call_first_node};
+use clippy_utils::source::{snippet_opt, snippet_with_context};
 use clippy_utils::sugg::Sugg;
-use if_chain::if_chain;
+use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::kw;
 use rustc_span::{sym, Span};
 
 declare_clippy_lint! {
@@ -44,55 +43,53 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
 
 impl<'tcx> LateLintPass<'tcx> for UselessFormat {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        let (format_args, call_site) = if_chain! {
-            if let Some(macro_call) = root_macro_call_first_node(cx, expr);
-            if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id);
-            if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn);
-            then {
-                (format_args, macro_call.span)
-            } else {
-                return
-            }
-        };
+        let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
+        if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) {
+            return;
+        }
+
+        find_format_args(cx, expr, macro_call.expn, |format_args| {
+            let mut applicability = Applicability::MachineApplicable;
+            let call_site = macro_call.span;
 
-        let mut applicability = Applicability::MachineApplicable;
-        if format_args.args.is_empty() {
-            match *format_args.format_string.parts {
-                [] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
-                [_] => {
+            match (format_args.arguments.all_args(), &format_args.template[..]) {
+                ([], []) => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
+                ([], [_]) => {
                     // Simulate macro expansion, converting {{ and }} to { and }.
-                    let s_expand = format_args.format_string.snippet.replace("{{", "{").replace("}}", "}");
+                    let Some(snippet) = snippet_opt(cx, format_args.span) else { return };
+                    let s_expand = snippet.replace("{{", "{").replace("}}", "}");
                     let sugg = format!("{s_expand}.to_string()");
                     span_useless_format(cx, call_site, sugg, applicability);
                 },
-                [..] => {},
-            }
-        } else if let [arg] = &*format_args.args {
-            let value = arg.param.value;
-            if_chain! {
-                if format_args.format_string.parts == [kw::Empty];
-                if arg.format.is_default();
-                if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
-                    ty::Adt(adt, _) => Some(adt.did()) == cx.tcx.lang_items().string(),
-                    ty::Str => true,
-                    _ => false,
-                };
-                then {
-                    let is_new_string = match value.kind {
-                        ExprKind::Binary(..) => true,
-                        ExprKind::MethodCall(path, ..) => path.ident.name == sym::to_string,
-                        _ => false,
-                    };
-                    let sugg = if is_new_string {
-                        snippet_with_context(cx, value.span, call_site.ctxt(), "..", &mut applicability).0.into_owned()
-                    } else {
-                        let sugg = Sugg::hir_with_context(cx, value, call_site.ctxt(), "<arg>", &mut applicability);
-                        format!("{}.to_string()", sugg.maybe_par())
-                    };
-                    span_useless_format(cx, call_site, sugg, applicability);
-                }
+                ([arg], [piece]) => {
+                    if let Ok(value) = find_format_arg_expr(expr, arg)
+                        && let FormatArgsPiece::Placeholder(placeholder) = piece
+                        && placeholder.format_trait == FormatTrait::Display
+                        && placeholder.format_options == FormatOptions::default()
+                        && match cx.typeck_results().expr_ty(value).peel_refs().kind() {
+                            ty::Adt(adt, _) => Some(adt.did()) == cx.tcx.lang_items().string(),
+                            ty::Str => true,
+                            _ => false,
+                        }
+                    {
+                        let is_new_string = match value.kind {
+                            ExprKind::Binary(..) => true,
+                            ExprKind::MethodCall(path, ..) => path.ident.name == sym::to_string,
+                            _ => false,
+                        };
+                        let sugg = if is_new_string {
+                            snippet_with_context(cx, value.span, call_site.ctxt(), "..", &mut applicability).0.into_owned()
+                        } else {
+                            let sugg = Sugg::hir_with_context(cx, value, call_site.ctxt(), "<arg>", &mut applicability);
+                            format!("{}.to_string()", sugg.maybe_par())
+                        };
+                        span_useless_format(cx, call_site, sugg, applicability);
+
+                    }
+                },
+                _ => {},
             }
-        };
+        });
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs
index c511d85e9cf..08e45ed7d0e 100644
--- a/src/tools/clippy/clippy_lints/src/format_args.rs
+++ b/src/tools/clippy/clippy_lints/src/format_args.rs
@@ -1,27 +1,31 @@
+use arrayvec::ArrayVec;
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::is_diag_trait_item;
-use clippy_utils::macros::FormatParamKind::{Implicit, Named, NamedInline, Numbered, Starred};
 use clippy_utils::macros::{
-    is_assert_macro, is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam,
-    FormatParamUsage,
+    find_format_arg_expr, find_format_args, format_arg_removal_span, format_placeholder_format_span, is_assert_macro,
+    is_format_macro, is_panic, root_macro_call, root_macro_call_first_node, FormatParamUsage,
 };
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{implements_trait, is_type_lang_item};
 use if_chain::if_chain;
 use itertools::Itertools;
+use rustc_ast::{
+    FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions,
+    FormatPlaceholder, FormatTrait,
+};
 use rustc_errors::{
     Applicability,
     SuggestionStyle::{CompletelyHidden, ShowCode},
 };
-use rustc_hir::{Expr, ExprKind, HirId, LangItem, QPath};
+use rustc_hir::{Expr, ExprKind, LangItem};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment};
 use rustc_middle::ty::Ty;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::def_id::DefId;
 use rustc_span::edition::Edition::Edition2021;
-use rustc_span::{sym, ExpnData, ExpnKind, Span, Symbol};
+use rustc_span::{sym, Span, Symbol};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -184,72 +188,79 @@ impl FormatArgs {
 
 impl<'tcx> LateLintPass<'tcx> for FormatArgs {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        if let Some(format_args) = FormatArgsExpn::parse(cx, expr)
-            && let expr_expn_data = expr.span.ctxt().outer_expn_data()
-            && let outermost_expn_data = outermost_expn_data(expr_expn_data)
-            && let Some(macro_def_id) = outermost_expn_data.macro_def_id
-            && is_format_macro(cx, macro_def_id)
-            && let ExpnKind::Macro(_, name) = outermost_expn_data.kind
-        {
-            for arg in &format_args.args {
-                check_unused_format_specifier(cx, arg);
-                if !arg.format.is_default() {
-                    continue;
-                }
-                if is_aliased(&format_args, arg.param.value.hir_id) {
-                    continue;
+        let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
+        if !is_format_macro(cx, macro_call.def_id) {
+            return;
+        }
+        let name = cx.tcx.item_name(macro_call.def_id);
+
+        find_format_args(cx, expr, macro_call.expn, |format_args| {
+            for piece in &format_args.template {
+                if let FormatArgsPiece::Placeholder(placeholder) = piece
+                    && let Ok(index) = placeholder.argument.index
+                    && let Some(arg) = format_args.arguments.all_args().get(index)
+                {
+                    let arg_expr = find_format_arg_expr(expr, arg);
+
+                    check_unused_format_specifier(cx, placeholder, arg_expr);
+
+                    if placeholder.format_trait != FormatTrait::Display
+                        || placeholder.format_options != FormatOptions::default()
+                        || is_aliased(format_args, index)
+                    {
+                        continue;
+                    }
+
+                    if let Ok(arg_hir_expr) = arg_expr {
+                        check_format_in_format_args(cx, macro_call.span, name, arg_hir_expr);
+                        check_to_string_in_format_args(cx, name, arg_hir_expr);
+                    }
                 }
-                check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value);
-                check_to_string_in_format_args(cx, name, arg.param.value);
             }
+
             if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) {
-                check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id, self.ignore_mixed);
+                check_uninlined_args(cx, format_args, macro_call.span, macro_call.def_id, self.ignore_mixed);
             }
-        }
+        });
     }
 
     extract_msrv_attr!(LateContext);
 }
 
-fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) {
-    let param_ty = cx.typeck_results().expr_ty(arg.param.value).peel_refs();
+fn check_unused_format_specifier(
+    cx: &LateContext<'_>,
+    placeholder: &FormatPlaceholder,
+    arg_expr: Result<&Expr<'_>, &rustc_ast::Expr>,
+) {
+    let ty_or_ast_expr = arg_expr.map(|expr| cx.typeck_results().expr_ty(expr).peel_refs());
 
-    if let Count::Implied(Some(mut span)) = arg.format.precision
-        && !span.is_empty()
-    {
-        span_lint_and_then(
-            cx,
-            UNUSED_FORMAT_SPECS,
-            span,
-            "empty precision specifier has no effect",
-            |diag| {
-                if param_ty.is_floating_point() {
-                    diag.note("a precision specifier is not required to format floats");
-                }
+    let is_format_args = match ty_or_ast_expr {
+        Ok(ty) => is_type_lang_item(cx, ty, LangItem::FormatArguments),
+        Err(expr) => matches!(expr.peel_parens_and_refs().kind, rustc_ast::ExprKind::FormatArgs(_)),
+    };
 
-                if arg.format.is_default() {
-                    // If there's no other specifiers remove the `:` too
-                    span = arg.format_span();
-                }
+    let options = &placeholder.format_options;
 
-                diag.span_suggestion_verbose(span, "remove the `.`", "", Applicability::MachineApplicable);
-            },
-        );
-    }
+    let arg_span = match arg_expr {
+        Ok(expr) => expr.span,
+        Err(expr) => expr.span,
+    };
 
-    if is_type_lang_item(cx, param_ty, LangItem::FormatArguments) && !arg.format.is_default_for_trait() {
+    if let Some(placeholder_span) = placeholder.span
+        && is_format_args
+        && *options != FormatOptions::default()
+    {
         span_lint_and_then(
             cx,
             UNUSED_FORMAT_SPECS,
-            arg.span,
+            placeholder_span,
             "format specifiers have no effect on `format_args!()`",
             |diag| {
-                let mut suggest_format = |spec, span| {
+                let mut suggest_format = |spec| {
                     let message = format!("for the {spec} to apply consider using `format!()`");
 
-                    if let Some(mac_call) = root_macro_call(arg.param.value.span)
+                    if let Some(mac_call) = root_macro_call(arg_span)
                         && cx.tcx.is_diagnostic_item(sym::format_args_macro, mac_call.def_id)
-                        && arg.span.eq_ctxt(mac_call.span)
                     {
                         diag.span_suggestion(
                             cx.sess().source_map().span_until_char(mac_call.span, '!'),
@@ -257,25 +268,27 @@ fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) {
                             "format",
                             Applicability::MaybeIncorrect,
                         );
-                    } else if let Some(span) = span {
-                        diag.span_help(span, message);
+                    } else {
+                        diag.help(message);
                     }
                 };
 
-                if !arg.format.width.is_implied() {
-                    suggest_format("width", arg.format.width.span());
+                if options.width.is_some() {
+                    suggest_format("width");
                 }
 
-                if !arg.format.precision.is_implied() {
-                    suggest_format("precision", arg.format.precision.span());
+                if options.precision.is_some() {
+                    suggest_format("precision");
                 }
 
-                diag.span_suggestion_verbose(
-                    arg.format_span(),
-                    "if the current behavior is intentional, remove the format specifiers",
-                    "",
-                    Applicability::MaybeIncorrect,
-                );
+                if let Some(format_span) = format_placeholder_format_span(placeholder) {
+                    diag.span_suggestion_verbose(
+                        format_span,
+                        "if the current behavior is intentional, remove the format specifiers",
+                        "",
+                        Applicability::MaybeIncorrect,
+                    );
+                }
             },
         );
     }
@@ -283,12 +296,12 @@ fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) {
 
 fn check_uninlined_args(
     cx: &LateContext<'_>,
-    args: &FormatArgsExpn<'_>,
+    args: &rustc_ast::FormatArgs,
     call_site: Span,
     def_id: DefId,
     ignore_mixed: bool,
 ) {
-    if args.format_string.span.from_expansion() {
+    if args.span.from_expansion() {
         return;
     }
     if call_site.edition() < Edition2021 && (is_panic(cx, def_id) || is_assert_macro(cx, def_id)) {
@@ -303,7 +316,13 @@ fn check_uninlined_args(
     // we cannot remove any other arguments in the format string,
     // because the index numbers might be wrong after inlining.
     // Example of an un-inlinable format:  print!("{}{1}", foo, 2)
-    if !args.params().all(|p| check_one_arg(args, &p, &mut fixes, ignore_mixed)) || fixes.is_empty() {
+    for (pos, usage) in format_arg_positions(args) {
+        if !check_one_arg(args, pos, usage, &mut fixes, ignore_mixed) {
+            return;
+        }
+    }
+
+    if fixes.is_empty() {
         return;
     }
 
@@ -332,47 +351,40 @@ fn check_uninlined_args(
 }
 
 fn check_one_arg(
-    args: &FormatArgsExpn<'_>,
-    param: &FormatParam<'_>,
+    args: &rustc_ast::FormatArgs,
+    pos: &FormatArgPosition,
+    usage: FormatParamUsage,
     fixes: &mut Vec<(Span, String)>,
     ignore_mixed: bool,
 ) -> bool {
-    if matches!(param.kind, Implicit | Starred | Named(_) | Numbered)
-        && let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind
-        && let [segment] = path.segments
+    let index = pos.index.unwrap();
+    let arg = &args.arguments.all_args()[index];
+
+    if !matches!(arg.kind, FormatArgumentKind::Captured(_))
+        && let rustc_ast::ExprKind::Path(None, path) = &arg.expr.kind
+        && let [segment] = path.segments.as_slice()
         && segment.args.is_none()
-        && let Some(arg_span) = args.value_with_prev_comma_span(param.value.hir_id)
+        && let Some(arg_span) = format_arg_removal_span(args, index)
+        && let Some(pos_span) = pos.span
     {
-        let replacement = match param.usage {
+        let replacement = match usage {
             FormatParamUsage::Argument => segment.ident.name.to_string(),
             FormatParamUsage::Width => format!("{}$", segment.ident.name),
             FormatParamUsage::Precision => format!(".{}$", segment.ident.name),
         };
-        fixes.push((param.span, replacement));
+        fixes.push((pos_span, replacement));
         fixes.push((arg_span, String::new()));
         true  // successful inlining, continue checking
     } else {
         // Do not continue inlining (return false) in case
         // * if we can't inline a numbered argument, e.g. `print!("{0} ...", foo.bar, ...)`
         // * if allow_mixed_uninlined_format_args is false and this arg hasn't been inlined already
-        param.kind != Numbered && (!ignore_mixed || matches!(param.kind, NamedInline(_)))
-    }
-}
-
-fn outermost_expn_data(expn_data: ExpnData) -> ExpnData {
-    if expn_data.call_site.from_expansion() {
-        outermost_expn_data(expn_data.call_site.ctxt().outer_expn_data())
-    } else {
-        expn_data
+        pos.kind != FormatArgPositionKind::Number
+            && (!ignore_mixed || matches!(arg.kind, FormatArgumentKind::Captured(_)))
     }
 }
 
-fn check_format_in_format_args(
-    cx: &LateContext<'_>,
-    call_site: Span,
-    name: Symbol,
-    arg: &Expr<'_>,
-) {
+fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &Expr<'_>) {
     let expn_data = arg.span.ctxt().outer_expn_data();
     if expn_data.call_site.from_expansion() {
         return;
@@ -443,9 +455,33 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
     }
 }
 
-/// Returns true if `hir_id` is referred to by multiple format params
-fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool {
-    args.params().filter(|param| param.value.hir_id == hir_id).at_most_one().is_err()
+fn format_arg_positions(
+    format_args: &rustc_ast::FormatArgs,
+) -> impl Iterator<Item = (&FormatArgPosition, FormatParamUsage)> {
+    format_args.template.iter().flat_map(|piece| match piece {
+        FormatArgsPiece::Placeholder(placeholder) => {
+            let mut positions = ArrayVec::<_, 3>::new();
+
+            positions.push((&placeholder.argument, FormatParamUsage::Argument));
+            if let Some(FormatCount::Argument(position)) = &placeholder.format_options.width {
+                positions.push((position, FormatParamUsage::Width));
+            }
+            if let Some(FormatCount::Argument(position)) = &placeholder.format_options.precision {
+                positions.push((position, FormatParamUsage::Precision));
+            }
+
+            positions
+        },
+        FormatArgsPiece::Literal(_) => ArrayVec::new(),
+    })
+}
+
+/// Returns true if the format argument at `index` is referred to by multiple format params
+fn is_aliased(format_args: &rustc_ast::FormatArgs, index: usize) -> bool {
+    format_arg_positions(format_args)
+        .filter(|(position, _)| position.index == Ok(index))
+        .at_most_one()
+        .is_err()
 }
 
 fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
@@ -455,7 +491,11 @@ where
     let mut n_total = 0;
     let mut n_needed = 0;
     loop {
-        if let Some(Adjustment { kind: Adjust::Deref(overloaded_deref), target }) = iter.next() {
+        if let Some(Adjustment {
+            kind: Adjust::Deref(overloaded_deref),
+            target,
+        }) = iter.next()
+        {
             n_total += 1;
             if overloaded_deref.is_some() {
                 n_needed = n_total;
diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs
index ed1342a5465..e3ddbfb5981 100644
--- a/src/tools/clippy/clippy_lints/src/format_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/format_impl.rs
@@ -1,11 +1,13 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArg, FormatArgsExpn};
+use clippy_utils::macros::{find_format_arg_expr, find_format_args, is_format_macro, root_macro_call_first_node};
 use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
 use if_chain::if_chain;
+use rustc_ast::{FormatArgsPiece, FormatTrait};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::Span;
 use rustc_span::{sym, symbol::kw, Symbol};
 
 declare_clippy_lint! {
@@ -89,7 +91,7 @@ declare_clippy_lint! {
 }
 
 #[derive(Clone, Copy)]
-struct FormatTrait {
+struct FormatTraitNames {
     /// e.g. `sym::Display`
     name: Symbol,
     /// `f` in `fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {}`
@@ -99,7 +101,7 @@ struct FormatTrait {
 #[derive(Default)]
 pub struct FormatImpl {
     // Whether we are inside Display or Debug trait impl - None for neither
-    format_trait_impl: Option<FormatTrait>,
+    format_trait_impl: Option<FormatTraitNames>,
 }
 
 impl FormatImpl {
@@ -161,43 +163,57 @@ fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) {
     }
 }
 
-fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, impl_trait: FormatTrait) {
+fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, impl_trait: FormatTraitNames) {
     // Check each arg in format calls - do we ever use Display on self (directly or via deref)?
-    if_chain! {
-        if let Some(outer_macro) = root_macro_call_first_node(cx, expr);
-        if let macro_def_id = outer_macro.def_id;
-        if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, outer_macro.expn);
-        if is_format_macro(cx, macro_def_id);
-        then {
-            for arg in format_args.args {
-                if arg.format.r#trait != impl_trait.name {
-                    continue;
+    if let Some(outer_macro) = root_macro_call_first_node(cx, expr)
+        && let macro_def_id = outer_macro.def_id
+        && is_format_macro(cx, macro_def_id)
+    {
+        find_format_args(cx, expr, outer_macro.expn, |format_args| {
+            for piece in &format_args.template {
+                if let FormatArgsPiece::Placeholder(placeholder) = piece
+                    && let trait_name = match placeholder.format_trait {
+                        FormatTrait::Display => sym::Display,
+                        FormatTrait::Debug => sym::Debug,
+                        FormatTrait::LowerExp => sym!(LowerExp),
+                        FormatTrait::UpperExp => sym!(UpperExp),
+                        FormatTrait::Octal => sym!(Octal),
+                        FormatTrait::Pointer => sym::Pointer,
+                        FormatTrait::Binary => sym!(Binary),
+                        FormatTrait::LowerHex => sym!(LowerHex),
+                        FormatTrait::UpperHex => sym!(UpperHex),
+                    }
+                    && trait_name == impl_trait.name
+                    && let Ok(index) = placeholder.argument.index
+                    && let Some(arg) = format_args.arguments.all_args().get(index)
+                    && let Ok(arg_expr) = find_format_arg_expr(expr, arg)
+                {
+                    check_format_arg_self(cx, expr.span, arg_expr, impl_trait);
                 }
-                check_format_arg_self(cx, expr, &arg, impl_trait);
             }
-        }
+        });
     }
 }
 
-fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArg<'_>, impl_trait: FormatTrait) {
+fn check_format_arg_self(cx: &LateContext<'_>, span: Span, arg: &Expr<'_>, impl_trait: FormatTraitNames) {
     // Handle multiple dereferencing of references e.g. &&self
     // Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl)
     // Since the argument to fmt is itself a reference: &self
-    let reference = peel_ref_operators(cx, arg.param.value);
+    let reference = peel_ref_operators(cx, arg);
     let map = cx.tcx.hir();
     // Is the reference self?
     if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) {
-        let FormatTrait { name, .. } = impl_trait;
+        let FormatTraitNames { name, .. } = impl_trait;
         span_lint(
             cx,
             RECURSIVE_FORMAT_IMPL,
-            expr.span,
+            span,
             &format!("using `self` as `{name}` in `impl {name}` will cause infinite recursion"),
         );
     }
 }
 
-fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait: FormatTrait) {
+fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait: FormatTraitNames) {
     if_chain! {
         if let Some(macro_call) = root_macro_call_first_node(cx, expr);
         if let Some(name) = cx.tcx.get_diagnostic_name(macro_call.def_id);
@@ -227,7 +243,7 @@ fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait:
     }
 }
 
-fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Option<FormatTrait> {
+fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Option<FormatTraitNames> {
     if_chain! {
         if impl_item.ident.name == sym::fmt;
         if let ImplItemKind::Fn(_, body_id) = impl_item.kind;
@@ -241,7 +257,7 @@ fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Optio
                 .and_then(|param| param.pat.simple_ident())
                 .map(|ident| ident.name);
 
-            Some(FormatTrait {
+            Some(FormatTraitNames {
                 name,
                 formatter_name,
             })
diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
index 1e9e826631c..d0ad2628264 100644
--- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
@@ -1,7 +1,9 @@
+use hir::FnSig;
 use rustc_ast::ast::Attribute;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefIdSet;
 use rustc_hir::{self as hir, def::Res, QPath};
+use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::{
     lint::in_external_macro,
@@ -27,7 +29,7 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>
         let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id);
         let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
         if let Some(attr) = attr {
-            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr);
+            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, sig);
         } else if is_public && !is_proc_macro(attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) {
             check_must_use_candidate(
                 cx,
@@ -49,7 +51,7 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp
         let attrs = cx.tcx.hir().attrs(item.hir_id());
         let attr = cx.tcx.get_attr(item.owner_id, sym::must_use);
         if let Some(attr) = attr {
-            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr);
+            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, sig);
         } else if is_public && !is_proc_macro(attrs) && trait_ref_of_method(cx, item.owner_id.def_id).is_none() {
             check_must_use_candidate(
                 cx,
@@ -72,7 +74,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr
         let attrs = cx.tcx.hir().attrs(item.hir_id());
         let attr = cx.tcx.get_attr(item.owner_id, sym::must_use);
         if let Some(attr) = attr {
-            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr);
+            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, sig);
         } else if let hir::TraitFn::Provided(eid) = *eid {
             let body = cx.tcx.hir().body(eid);
             if attr.is_none() && is_public && !is_proc_macro(attrs) {
@@ -97,6 +99,7 @@ fn check_needless_must_use(
     item_span: Span,
     fn_header_span: Span,
     attr: &Attribute,
+    sig: &FnSig<'_>,
 ) {
     if in_external_macro(cx.sess(), item_span) {
         return;
@@ -112,6 +115,15 @@ fn check_needless_must_use(
             },
         );
     } else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) {
+        // Ignore async functions unless Future::Output type is a must_use type
+        if sig.header.is_async() {
+            let infcx = cx.tcx.infer_ctxt().build();
+            if let Some(future_ty) = infcx.get_impl_future_output_ty(return_ty(cx, item_id))
+			&& !is_must_use_ty(cx, future_ty) {
+				return;
+			}
+        }
+
         span_lint_and_help(
             cx,
             DOUBLE_MUST_USE,
diff --git a/src/tools/clippy/clippy_lints/src/items_after_statements.rs b/src/tools/clippy/clippy_lints/src/items_after_statements.rs
index 46d439b4497..a7ec57e2850 100644
--- a/src/tools/clippy/clippy_lints/src/items_after_statements.rs
+++ b/src/tools/clippy/clippy_lints/src/items_after_statements.rs
@@ -1,8 +1,8 @@
 //! lint when items are used after statements
 
-use clippy_utils::diagnostics::span_lint;
-use rustc_ast::ast::{Block, ItemKind, StmtKind};
-use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
+use clippy_utils::diagnostics::span_lint_hir;
+use rustc_hir::{Block, ItemKind, StmtKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
@@ -52,33 +52,34 @@ declare_clippy_lint! {
 
 declare_lint_pass!(ItemsAfterStatements => [ITEMS_AFTER_STATEMENTS]);
 
-impl EarlyLintPass for ItemsAfterStatements {
-    fn check_block(&mut self, cx: &EarlyContext<'_>, item: &Block) {
-        if in_external_macro(cx.sess(), item.span) {
+impl LateLintPass<'_> for ItemsAfterStatements {
+    fn check_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>) {
+        if in_external_macro(cx.sess(), block.span) {
             return;
         }
 
-        // skip initial items and trailing semicolons
-        let stmts = item
+        // skip initial items
+        let stmts = block
             .stmts
             .iter()
-            .map(|stmt| &stmt.kind)
-            .skip_while(|s| matches!(**s, StmtKind::Item(..) | StmtKind::Empty));
+            .skip_while(|stmt| matches!(stmt.kind, StmtKind::Item(..)));
 
         // lint on all further items
         for stmt in stmts {
-            if let StmtKind::Item(ref it) = *stmt {
-                if in_external_macro(cx.sess(), it.span) {
+            if let StmtKind::Item(item_id) = stmt.kind {
+                let item = cx.tcx.hir().item(item_id);
+                if in_external_macro(cx.sess(), item.span) || !item.span.eq_ctxt(block.span) {
                     return;
                 }
-                if let ItemKind::MacroDef(..) = it.kind {
+                if let ItemKind::Macro(..) = item.kind {
                     // do not lint `macro_rules`, but continue processing further statements
                     continue;
                 }
-                span_lint(
+                span_lint_hir(
                     cx,
                     ITEMS_AFTER_STATEMENTS,
-                    it.span,
+                    item.hir_id(),
+                    item.span,
                     "adding items after statements is confusing, since items exist from the \
                      start of the scope",
                 );
diff --git a/src/tools/clippy/clippy_lints/src/large_futures.rs b/src/tools/clippy/clippy_lints/src/large_futures.rs
new file mode 100644
index 00000000000..1b054481371
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/large_futures.rs
@@ -0,0 +1,87 @@
+use clippy_utils::source::snippet;
+use clippy_utils::{diagnostics::span_lint_and_sugg, ty::implements_trait};
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_target::abi::Size;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// It checks for the size of a `Future` created by `async fn` or `async {}`.
+    ///
+    /// ### Why is this bad?
+    /// Due to the current [unideal implemention](https://github.com/rust-lang/rust/issues/69826) of `Generator`,
+    /// large size of a `Future` may cause stack overflows.
+    ///
+    /// ### Example
+    /// ```rust
+    /// async fn wait(f: impl std::future::Future<Output = ()>) {}
+    ///
+    /// async fn big_fut(arg: [u8; 1024]) {}
+    ///
+    /// pub async fn test() {
+    ///     let fut = big_fut([0u8; 1024]);
+    ///     wait(fut).await;
+    /// }
+    /// ```
+    ///
+    /// `Box::pin` the big future instead.
+    ///
+    /// ```rust
+    /// async fn wait(f: impl std::future::Future<Output = ()>) {}
+    ///
+    /// async fn big_fut(arg: [u8; 1024]) {}
+    ///
+    /// pub async fn test() {
+    ///     let fut = Box::pin(big_fut([0u8; 1024]));
+    ///     wait(fut).await;
+    /// }
+    /// ```
+    #[clippy::version = "1.68.0"]
+    pub LARGE_FUTURES,
+    pedantic,
+    "large future may lead to unexpected stack overflows"
+}
+
+#[derive(Copy, Clone)]
+pub struct LargeFuture {
+    future_size_threshold: u64,
+}
+
+impl LargeFuture {
+    pub fn new(future_size_threshold: u64) -> Self {
+        Self { future_size_threshold }
+    }
+}
+
+impl_lint_pass!(LargeFuture => [LARGE_FUTURES]);
+
+impl<'tcx> LateLintPass<'tcx> for LargeFuture {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+        if matches!(expr.span.ctxt().outer_expn_data().kind, rustc_span::ExpnKind::Macro(..)) {
+            return;
+        }
+        if let ExprKind::Match(expr, _, MatchSource::AwaitDesugar) = expr.kind {
+            if let ExprKind::Call(func, [expr, ..]) = expr.kind
+                && let ExprKind::Path(QPath::LangItem(LangItem::IntoFutureIntoFuture, ..)) = func.kind
+                && let ty = cx.typeck_results().expr_ty(expr)
+                && let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait()
+                && implements_trait(cx, ty, future_trait_def_id, &[])
+                && let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty))
+                && let size = layout.layout.size()
+                && size >= Size::from_bytes(self.future_size_threshold)
+            {
+                span_lint_and_sugg(
+                    cx,
+                    LARGE_FUTURES,
+                    expr.span,
+                    &format!("large future with a size of {} bytes", size.bytes()),
+                    "consider `Box::pin` on it",
+                    format!("Box::pin({})", snippet(cx, expr.span, "..")),
+                    Applicability::Unspecified,
+                );
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 3da7f95c1b9..b0ec14855e7 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -1,7 +1,6 @@
 #![feature(array_windows)]
 #![feature(binary_heap_into_iter_sorted)]
 #![feature(box_patterns)]
-#![feature(drain_filter)]
 #![feature(if_let_guard)]
 #![feature(iter_intersperse)]
 #![feature(let_chains)]
@@ -162,6 +161,7 @@ mod items_after_statements;
 mod iter_not_returning_iterator;
 mod large_const_arrays;
 mod large_enum_variant;
+mod large_futures;
 mod large_include_file;
 mod large_stack_arrays;
 mod len_zero;
@@ -169,6 +169,7 @@ mod let_if_seq;
 mod let_underscore;
 mod let_with_type_underscore;
 mod lifetimes;
+mod lines_filter_map_ok;
 mod literal_representation;
 mod loops;
 mod macro_use;
@@ -183,6 +184,7 @@ mod manual_main_separator_str;
 mod manual_non_exhaustive;
 mod manual_rem_euclid;
 mod manual_retain;
+mod manual_slice_size_calculation;
 mod manual_string_new;
 mod manual_strip;
 mod map_unit_fn;
@@ -281,6 +283,7 @@ mod slow_vector_initialization;
 mod std_instead_of_core;
 mod strings;
 mod strlen_on_c_strings;
+mod suspicious_doc_comments;
 mod suspicious_operation_groupings;
 mod suspicious_trait_impl;
 mod suspicious_xor_used_as_pow;
@@ -288,6 +291,7 @@ mod swap;
 mod swap_ptr_to_ref;
 mod tabs_in_doc_comments;
 mod temporary_assignment;
+mod tests_outside_test_module;
 mod to_digit_is_some;
 mod trailing_empty_array;
 mod trait_bounds;
@@ -299,6 +303,7 @@ mod uninit_vec;
 mod unit_return_expecting_ord;
 mod unit_types;
 mod unnamed_address;
+mod unnecessary_box_returns;
 mod unnecessary_owned_empty_strings;
 mod unnecessary_self_imports;
 mod unnecessary_struct_initialization;
@@ -344,13 +349,17 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Se
 }
 
 #[doc(hidden)]
-pub fn read_conf(sess: &Session, path: &io::Result<Option<PathBuf>>) -> Conf {
+pub fn read_conf(sess: &Session, path: &io::Result<(Option<PathBuf>, Vec<String>)>) -> Conf {
+    if let Ok((_, warnings)) = path {
+        for warning in warnings {
+            sess.warn(warning);
+        }
+    }
     let file_name = match path {
-        Ok(Some(path)) => path,
-        Ok(None) => return Conf::default(),
+        Ok((Some(path), _)) => path,
+        Ok((None, _)) => return Conf::default(),
         Err(error) => {
-            sess.struct_err(format!("error finding Clippy's configuration file: {error}"))
-                .emit();
+            sess.err(format!("error finding Clippy's configuration file: {error}"));
             return Conf::default();
         },
     };
@@ -746,7 +755,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(|| Box::new(unused_unit::UnusedUnit));
     store.register_late_pass(|_| Box::new(returns::Return));
     store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf));
-    store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements));
+    store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements));
     store.register_early_pass(|| Box::new(precedence::Precedence));
     store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals));
     store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue));
@@ -808,6 +817,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv())));
     store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse));
     store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
+    let future_size_threshold = conf.future_size_threshold;
+    store.register_late_pass(move |_| Box::new(large_futures::LargeFuture::new(future_size_threshold)));
     store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex));
     store.register_late_pass(|_| Box::new(if_not_else::IfNotElse));
     store.register_late_pass(|_| Box::new(equatable_if_let::PatternEquality));
@@ -934,11 +945,20 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi));
     store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead));
     store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
-    store.register_early_pass(|| Box::new(redundant_async_block::RedundantAsyncBlock));
+    store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock));
     store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped));
     store.register_late_pass(|_| Box::new(allow_attributes::AllowAttribute));
     store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(msrv())));
     store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct));
+    store.register_late_pass(move |_| {
+        Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(
+            avoid_breaking_exported_api,
+        ))
+    });
+    store.register_late_pass(|_| Box::new(lines_filter_map_ok::LinesFilterMapOk));
+    store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule));
+    store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation));
+    store.register_early_pass(|| Box::new(suspicious_doc_comments::SuspiciousDocComments));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs
new file mode 100644
index 00000000000..b0f9276475d
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs
@@ -0,0 +1,100 @@
+use clippy_utils::{
+    diagnostics::span_lint_and_then, is_diag_item_method, is_trait_method, match_def_path, path_to_local_id, paths,
+    ty::match_type,
+};
+use rustc_errors::Applicability;
+use rustc_hir::{Body, Closure, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Detect uses of `lines.filter_map(Result::ok)` or `lines.flat_map(Result::ok)`
+    /// when `lines` has type `std::io::Lines`.
+    ///
+    /// ### Why is this bad?
+    /// `Lines` instances might produce a never-ending stream of `Err`, in which case
+    /// `filter_map(Result::ok)` will enter an infinite loop while waiting for an
+    /// `Ok` variant. Calling `next()` once is sufficient to enter the infinite loop,
+    /// even in the absence of explicit loops in the user code.
+    ///
+    /// This situation can arise when working with user-provided paths. On some platforms,
+    /// `std::fs::File::open(path)` might return `Ok(fs)` even when `path` is a directory,
+    /// but any later attempt to read from `fs` will return an error.
+    ///
+    /// ### Known problems
+    /// This lint suggests replacing `filter_map()` or `flat_map()` applied to a `Lines`
+    /// instance in all cases. There two cases where the suggestion might not be
+    /// appropriate or necessary:
+    ///
+    /// - If the `Lines` instance can never produce any error, or if an error is produced
+    ///   only once just before terminating the iterator, using `map_while()` is not
+    ///   necessary but will not do any harm.
+    /// - If the `Lines` instance can produce intermittent errors then recover and produce
+    ///   successful results, using `map_while()` would stop at the first error.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # use std::{fs::File, io::{self, BufRead, BufReader}};
+    /// # let _ = || -> io::Result<()> {
+    /// let mut lines = BufReader::new(File::open("some-path")?).lines().filter_map(Result::ok);
+    /// // If "some-path" points to a directory, the next statement never terminates:
+    /// let first_line: Option<String> = lines.next();
+    /// # Ok(()) };
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # use std::{fs::File, io::{self, BufRead, BufReader}};
+    /// # let _ = || -> io::Result<()> {
+    /// let mut lines = BufReader::new(File::open("some-path")?).lines().map_while(Result::ok);
+    /// let first_line: Option<String> = lines.next();
+    /// # Ok(()) };
+    /// ```
+    #[clippy::version = "1.70.0"]
+    pub LINES_FILTER_MAP_OK,
+    suspicious,
+    "filtering `std::io::Lines` with `filter_map()` or `flat_map()` might cause an infinite loop"
+}
+declare_lint_pass!(LinesFilterMapOk => [LINES_FILTER_MAP_OK]);
+
+impl LateLintPass<'_> for LinesFilterMapOk {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        if let ExprKind::MethodCall(fm_method, fm_receiver, [fm_arg], fm_span) = expr.kind &&
+            is_trait_method(cx, expr, sym::Iterator) &&
+            (fm_method.ident.as_str() == "filter_map" || fm_method.ident.as_str() == "flat_map") &&
+            match_type(cx, cx.typeck_results().expr_ty_adjusted(fm_receiver), &paths::STD_IO_LINES)
+        {
+            let lint = match &fm_arg.kind {
+                // Detect `Result::ok`
+                ExprKind::Path(qpath) =>
+                    cx.qpath_res(qpath, fm_arg.hir_id).opt_def_id().map(|did|
+                        match_def_path(cx, did, &paths::CORE_RESULT_OK_METHOD)).unwrap_or_default(),
+                // Detect `|x| x.ok()`
+                ExprKind::Closure(Closure { body, .. }) =>
+                    if let Body { params: [param], value, .. } = cx.tcx.hir().body(*body) &&
+                        let ExprKind::MethodCall(method, receiver, [], _) = value.kind &&
+                        path_to_local_id(receiver, param.pat.hir_id) &&
+                        let Some(method_did) = cx.typeck_results().type_dependent_def_id(value.hir_id)
+                    {
+                        is_diag_item_method(cx, method_did, sym::Result) && method.ident.as_str() == "ok"
+                    } else {
+                        false
+                    }
+                _ => false,
+            };
+            if lint {
+                span_lint_and_then(cx,
+                    LINES_FILTER_MAP_OK,
+                    fm_span,
+                    &format!("`{}()` will run forever if the iterator repeatedly produces an `Err`", fm_method.ident),
+                    |diag| {
+                        diag.span_note(
+                            fm_receiver.span,
+                            "this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error");
+                        diag.span_suggestion(fm_span, "replace with", "map_while(Result::ok)", Applicability::MaybeIncorrect);
+                    });
+                }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
new file mode 100644
index 00000000000..92ee79453a3
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
@@ -0,0 +1,93 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::{expr_or_init, in_constant};
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// When `a` is `&[T]`, detect `a.len() * size_of::<T>()` and suggest `size_of_val(a)`
+    /// instead.
+    ///
+    /// ### Why is this better?
+    /// * Shorter to write
+    /// * Removes the need for the human and the compiler to worry about overflow in the
+    ///   multiplication
+    /// * Potentially faster at runtime as rust emits special no-wrapping flags when it
+    ///   calculates the byte length
+    /// * Less turbofishing
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let data : &[i32] = &[1, 2, 3];
+    /// let newlen = data.len() * std::mem::size_of::<i32>();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # let data : &[i32] = &[1, 2, 3];
+    /// let newlen = std::mem::size_of_val(data);
+    /// ```
+    #[clippy::version = "1.70.0"]
+    pub MANUAL_SLICE_SIZE_CALCULATION,
+    complexity,
+    "manual slice size calculation"
+}
+declare_lint_pass!(ManualSliceSizeCalculation => [MANUAL_SLICE_SIZE_CALCULATION]);
+
+impl<'tcx> LateLintPass<'tcx> for ManualSliceSizeCalculation {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+        // Does not apply inside const because size_of_value is not cost in stable.
+        if !in_constant(cx, expr.hir_id)
+            && let ExprKind::Binary(ref op, left, right) = expr.kind
+            && BinOpKind::Mul == op.node
+            && let Some(_receiver) = simplify(cx, left, right)
+        {
+            span_lint_and_help(
+                cx,
+                MANUAL_SLICE_SIZE_CALCULATION,
+                expr.span,
+                "manual slice size calculation",
+                None,
+                "consider using std::mem::size_of_value instead");
+        }
+    }
+}
+
+fn simplify<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr1: &'tcx Expr<'tcx>,
+    expr2: &'tcx Expr<'tcx>,
+) -> Option<&'tcx Expr<'tcx>> {
+    let expr1 = expr_or_init(cx, expr1);
+    let expr2 = expr_or_init(cx, expr2);
+
+    simplify_half(cx, expr1, expr2).or_else(|| simplify_half(cx, expr2, expr1))
+}
+
+fn simplify_half<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr1: &'tcx Expr<'tcx>,
+    expr2: &'tcx Expr<'tcx>,
+) -> Option<&'tcx Expr<'tcx>> {
+    if
+        // expr1 is `[T1].len()`?
+        let ExprKind::MethodCall(method_path, receiver, _, _) = expr1.kind
+        && method_path.ident.name == sym::len
+        && let receiver_ty = cx.typeck_results().expr_ty(receiver)
+        && let ty::Slice(ty1) = receiver_ty.peel_refs().kind()
+        // expr2 is `size_of::<T2>()`?
+        && let ExprKind::Call(func, _) = expr2.kind
+        && let ExprKind::Path(ref func_qpath) = func.kind
+        && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id()
+        && cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id)
+        && let Some(ty2) = cx.typeck_results().node_substs(func.hir_id).types().next()
+        // T1 == T2?
+        && *ty1 == ty2
+    {
+        Some(receiver)
+    } else {
+        None
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs
index 35024ec1224..8a921d4af16 100644
--- a/src/tools/clippy/clippy_lints/src/mem_replace.rs
+++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs
@@ -1,12 +1,13 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_non_aggregate_primitive_type;
-use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res};
+use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res, peel_ref_operators};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::OptionNone;
-use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
+use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -101,40 +102,26 @@ declare_clippy_lint! {
 impl_lint_pass!(MemReplace =>
     [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);
 
-fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
-    // Check that second argument is `Option::None`
-    if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) {
-        // Since this is a late pass (already type-checked),
-        // and we already know that the second argument is an
-        // `Option`, we do not need to check the first
-        // argument's type. All that's left is to get
-        // replacee's path.
-        let replaced_path = match dest.kind {
-            ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, replaced) => {
-                if let ExprKind::Path(QPath::Resolved(None, replaced_path)) = replaced.kind {
-                    replaced_path
-                } else {
-                    return;
-                }
-            },
-            ExprKind::Path(QPath::Resolved(None, replaced_path)) => replaced_path,
-            _ => return,
-        };
-
-        let mut applicability = Applicability::MachineApplicable;
-        span_lint_and_sugg(
-            cx,
-            MEM_REPLACE_OPTION_WITH_NONE,
-            expr_span,
-            "replacing an `Option` with `None`",
-            "consider `Option::take()` instead",
-            format!(
-                "{}.take()",
-                snippet_with_applicability(cx, replaced_path.span, "", &mut applicability)
-            ),
-            applicability,
-        );
-    }
+fn check_replace_option_with_none(cx: &LateContext<'_>, dest: &Expr<'_>, expr_span: Span) {
+    // Since this is a late pass (already type-checked),
+    // and we already know that the second argument is an
+    // `Option`, we do not need to check the first
+    // argument's type. All that's left is to get
+    // the replacee's expr after peeling off the `&mut`
+    let sugg_expr = peel_ref_operators(cx, dest);
+    let mut applicability = Applicability::MachineApplicable;
+    span_lint_and_sugg(
+        cx,
+        MEM_REPLACE_OPTION_WITH_NONE,
+        expr_span,
+        "replacing an `Option` with `None`",
+        "consider `Option::take()` instead",
+        format!(
+            "{}.take()",
+            Sugg::hir_with_context(cx, sugg_expr, expr_span.ctxt(), "", &mut applicability).maybe_par()
+        ),
+        applicability,
+    );
 }
 
 fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
@@ -200,10 +187,6 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
     if is_non_aggregate_primitive_type(expr_type) {
         return;
     }
-    // disable lint for Option since it is covered in another lint
-    if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) {
-        return;
-    }
     if is_default_equivalent(cx, src) && !in_external_macro(cx.tcx.sess, expr_span) {
         span_lint_and_then(
             cx,
@@ -246,11 +229,13 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace {
             if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
             if cx.tcx.is_diagnostic_item(sym::mem_replace, def_id);
             then {
-                check_replace_option_with_none(cx, src, dest, expr.span);
-                check_replace_with_uninit(cx, src, dest, expr.span);
-                if self.msrv.meets(msrvs::MEM_TAKE) {
+                // Check that second argument is `Option::None`
+                if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) {
+                    check_replace_option_with_none(cx, dest, expr.span);
+                } else if self.msrv.meets(msrvs::MEM_TAKE) {
                     check_replace_with_default(cx, src, dest, expr.span);
                 }
+                check_replace_with_uninit(cx, src, dest, expr.span);
             }
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs
new file mode 100644
index 00000000000..67ad58d5a8c
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs
@@ -0,0 +1,53 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_range_full;
+use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::{Expr, ExprKind, LangItem, QPath};
+use rustc_lint::LateContext;
+use rustc_span::symbol::sym;
+use rustc_span::Span;
+
+use super::CLEAR_WITH_DRAIN;
+
+// Add `String` here when it is added to diagnostic items
+const ACCEPTABLE_TYPES_WITH_ARG: [rustc_span::Symbol; 2] = [sym::Vec, sym::VecDeque];
+
+const ACCEPTABLE_TYPES_WITHOUT_ARG: [rustc_span::Symbol; 3] = [sym::BinaryHeap, sym::HashMap, sym::HashSet];
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: Option<&Expr<'_>>) {
+    if let Some(arg) = arg {
+        if match_acceptable_type(cx, recv, &ACCEPTABLE_TYPES_WITH_ARG)
+            && let ExprKind::Path(QPath::Resolved(None, container_path)) = recv.kind
+            && is_range_full(cx, arg, Some(container_path))
+        {
+            suggest(cx, expr, recv, span);
+        }
+    } else if match_acceptable_type(cx, recv, &ACCEPTABLE_TYPES_WITHOUT_ARG) {
+        suggest(cx, expr, recv, span);
+    }
+}
+
+fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, types: &[rustc_span::Symbol]) -> bool {
+    let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs();
+    types.iter().any(|&ty| is_type_diagnostic_item(cx, expr_ty, ty))
+    // String type is a lang item but not a diagnostic item for now so we need a separate check
+        || is_type_lang_item(cx, expr_ty, LangItem::String)
+}
+
+fn suggest(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span) {
+    if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def()
+    // Use `opt_item_name` while `String` is not a diagnostic item
+        && let Some(ty_name) = cx.tcx.opt_item_name(adt.did())
+    {
+        span_lint_and_sugg(
+            cx,
+            CLEAR_WITH_DRAIN,
+            span.with_hi(expr.span.hi()),
+            &format!("`drain` used to clear a `{ty_name}`"),
+            "try",
+            "clear()".to_string(),
+            Applicability::MachineApplicable,
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
index a22285058d4..92d21bb8932 100644
--- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
+use clippy_utils::macros::{find_format_args, format_args_inputs_span, root_macro_call_first_node};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
 use rustc_errors::Applicability;
@@ -136,18 +136,19 @@ pub(super) fn check<'tcx>(
         if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) {
             return;
         }
-        let Some(format_args) = FormatArgsExpn::find_nested(cx, arg_root, macro_call.expn) else { return };
-        let span = format_args.inputs_span();
-        let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
-        span_lint_and_sugg(
-            cx,
-            EXPECT_FUN_CALL,
-            span_replace_word,
-            &format!("use of `{name}` followed by a function call"),
-            "try this",
-            format!("unwrap_or_else({closure_args} panic!({sugg}))"),
-            applicability,
-        );
+        find_format_args(cx, arg_root, macro_call.expn, |format_args| {
+            let span = format_args_inputs_span(format_args);
+            let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
+            span_lint_and_sugg(
+                cx,
+                EXPECT_FUN_CALL,
+                span_replace_word,
+                &format!("use of `{name}` followed by a function call"),
+                "try this",
+                format!("unwrap_or_else({closure_args} panic!({sugg}))"),
+                applicability,
+            );
+        });
         return;
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs
index 3da230e12d7..f6772c5c6b3 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs
@@ -1,7 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::higher::Range;
-use clippy_utils::is_integer_const;
-use rustc_ast::ast::RangeLimits;
+use clippy_utils::is_range_full;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::LateContext;
@@ -15,8 +13,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span
         && let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def()
         && let Some(ty_name) = cx.tcx.get_diagnostic_name(adt.did())
         && matches!(ty_name, sym::Vec | sym::VecDeque)
-        && let Some(range) = Range::hir(arg)
-        && is_full_range(cx, recv, range)
+        && let ExprKind::Path(QPath::Resolved(None, container_path)) = recv.kind
+        && is_range_full(cx, arg, Some(container_path))
     {
         span_lint_and_sugg(
             cx,
@@ -29,19 +27,3 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span
         );
     };
 }
-
-fn is_full_range(cx: &LateContext<'_>, container: &Expr<'_>, range: Range<'_>) -> bool {
-    range.start.map_or(true, |e| is_integer_const(cx, e, 0))
-        && range.end.map_or(true, |e| {
-            if range.limits == RangeLimits::HalfOpen
-                && let ExprKind::Path(QPath::Resolved(None, container_path)) = container.kind
-                && let ExprKind::MethodCall(name, self_arg, [], _) = e.kind
-                && name.ident.name == sym::len
-                && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
-            {
-                container_path.res == path.res
-            } else {
-                false
-            }
-        })
-}
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 56e3988bf09..64bf55ba24c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -9,6 +9,7 @@ mod chars_last_cmp;
 mod chars_last_cmp_with_unwrap;
 mod chars_next_cmp;
 mod chars_next_cmp_with_unwrap;
+mod clear_with_drain;
 mod clone_on_copy;
 mod clone_on_ref_ptr;
 mod cloned_instead_of_copied;
@@ -110,7 +111,7 @@ use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_
 use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, return_ty};
 use if_chain::if_chain;
 use rustc_hir as hir;
-use rustc_hir::{Expr, ExprKind, TraitItem, TraitItemKind};
+use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind};
 use rustc_hir_analysis::hir_ty_to_ty;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -3190,6 +3191,31 @@ declare_clippy_lint! {
     "single command line argument that looks like it should be multiple arguments"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for usage of `.drain(..)` for the sole purpose of clearing a container.
+    ///
+    /// ### Why is this bad?
+    /// This creates an unnecessary iterator that is dropped immediately.
+    ///
+    /// Calling `.clear()` also makes the intent clearer.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let mut v = vec![1, 2, 3];
+    /// v.drain(..);
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let mut v = vec![1, 2, 3];
+    /// v.clear();
+    /// ```
+    #[clippy::version = "1.69.0"]
+    pub CLEAR_WITH_DRAIN,
+    nursery,
+    "calling `drain` in order to `clear` a container"
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Msrv,
@@ -3318,6 +3344,7 @@ impl_lint_pass!(Methods => [
     SEEK_TO_START_INSTEAD_OF_REWIND,
     NEEDLESS_COLLECT,
     SUSPICIOUS_COMMAND_ARG_SPACE,
+    CLEAR_WITH_DRAIN,
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -3562,8 +3589,15 @@ impl Methods {
                     Some(("bytes", recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2),
                     _ => {},
                 },
-                ("drain", [arg]) => {
-                    iter_with_drain::check(cx, expr, recv, span, arg);
+                ("drain", ..) => {
+                    if let Node::Stmt(Stmt { hir_id: _, kind, .. }) = cx.tcx.hir().get_parent(expr.hir_id)
+                        && matches!(kind, StmtKind::Semi(_))
+                        && args.len() <= 1
+                    {
+                        clear_with_drain::check(cx, expr, recv, span, args.first());
+                    } else if let [arg] = args {
+                        iter_with_drain::check(cx, expr, recv, span, arg);
+                    }
                 },
                 ("ends_with", [arg]) => {
                     if let ExprKind::MethodCall(.., span) = expr.kind {
diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
index 87bd007a26a..f1831a30461 100644
--- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
@@ -41,6 +41,7 @@ declare_clippy_lint! {
     /// can't be const as it calls a non-const function. Making `a` const and running Clippy again,
     /// will suggest to make `b` const, too.
     ///
+    /// If you are marking a public function with `const`, removing it again will break API compatibility.
     /// ### Example
     /// ```rust
     /// # struct Foo {
diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
index 25e8de94863..e5713735672 100644
--- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
@@ -154,10 +154,18 @@ impl ArithmeticSideEffects {
                 Self::literal_integer(cx, actual_rhs),
             ) {
                 (None, None) => false,
-                (None, Some(n)) | (Some(n), None) => match (&op.node, n) {
+                (None, Some(n)) => match (&op.node, n) {
                     // Division and module are always valid if applied to non-zero integers
                     (hir::BinOpKind::Div | hir::BinOpKind::Rem, local_n) if local_n != 0 => true,
-                    // Addition or subtracting zeros is always a no-op
+                    // Adding or subtracting zeros is always a no-op
+                    (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
+                    // Multiplication by 1 or 0 will never overflow
+                    | (hir::BinOpKind::Mul, 0 | 1)
+                    => true,
+                    _ => false,
+                },
+                (Some(n), None) => match (&op.node, n) {
+                    // Adding or subtracting zeros is always a no-op
                     (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
                     // Multiplication by 1 or 0 will never overflow
                     | (hir::BinOpKind::Mul, 0 | 1)
diff --git a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
index 5ac203665d0..a0f831764d0 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
@@ -1,8 +1,15 @@
-use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet};
-use rustc_ast::ast::{Expr, ExprKind, Stmt, StmtKind};
-use rustc_ast::visit::Visitor as AstVisitor;
+use std::ops::ControlFlow;
+
+use clippy_utils::{
+    diagnostics::span_lint_and_sugg,
+    peel_blocks,
+    source::{snippet, walk_span_to_context},
+    visitors::for_each_expr,
+};
 use rustc_errors::Applicability;
-use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_hir::{AsyncGeneratorKind, Closure, Expr, ExprKind, GeneratorKind, MatchSource};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::{lint::in_external_macro, ty::UpvarCapture};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
 declare_clippy_lint! {
@@ -14,106 +21,88 @@ declare_clippy_lint! {
     ///
     /// ### Example
     /// ```rust
-    /// async fn f() -> i32 {
-    ///     1 + 2
-    /// }
-    ///
+    /// let f = async {
+    ///    1 + 2
+    /// };
     /// let fut = async {
-    ///     f().await
+    ///     f.await
     /// };
     /// ```
     /// Use instead:
     /// ```rust
-    /// async fn f() -> i32 {
-    ///     1 + 2
-    /// }
-    ///
-    /// let fut = f();
+    /// let f = async {
+    ///    1 + 2
+    /// };
+    /// let fut = f;
     /// ```
     #[clippy::version = "1.69.0"]
     pub REDUNDANT_ASYNC_BLOCK,
-    nursery,
+    complexity,
     "`async { future.await }` can be replaced by `future`"
 }
 declare_lint_pass!(RedundantAsyncBlock => [REDUNDANT_ASYNC_BLOCK]);
 
-impl EarlyLintPass for RedundantAsyncBlock {
-    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
-        if expr.span.from_expansion() {
-            return;
-        }
-        if let ExprKind::Async(_, block) = &expr.kind && block.stmts.len() == 1 &&
-            let Some(Stmt { kind: StmtKind::Expr(last), .. }) = block.stmts.last() &&
-            let ExprKind::Await(future) = &last.kind &&
-            !future.span.from_expansion() &&
-            !await_in_expr(future)
+impl<'tcx> LateLintPass<'tcx> for RedundantAsyncBlock {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        let span = expr.span;
+        if !in_external_macro(cx.tcx.sess, span) &&
+            let Some(body_expr) = desugar_async_block(cx, expr) &&
+            let Some(expr) = desugar_await(peel_blocks(body_expr)) &&
+            // The await prefix must not come from a macro as its content could change in the future.
+            expr.span.ctxt() == body_expr.span.ctxt() &&
+            // An async block does not have immediate side-effects from a `.await` point-of-view.
+            (!expr.can_have_side_effects() || desugar_async_block(cx, expr).is_some()) &&
+            let Some(shortened_span) = walk_span_to_context(expr.span, span.ctxt())
         {
-            if captures_value(last) {
-                // If the async block captures variables then there is no equivalence.
-                return;
-            }
-
             span_lint_and_sugg(
                 cx,
                 REDUNDANT_ASYNC_BLOCK,
-                expr.span,
+                span,
                 "this async expression only awaits a single future",
                 "you can reduce it to",
-                snippet(cx, future.span, "..").into_owned(),
+                snippet(cx, shortened_span, "..").into_owned(),
                 Applicability::MachineApplicable,
             );
         }
     }
 }
 
-/// Check whether an expression contains `.await`
-fn await_in_expr(expr: &Expr) -> bool {
-    let mut detector = AwaitDetector::default();
-    detector.visit_expr(expr);
-    detector.await_found
-}
-
-#[derive(Default)]
-struct AwaitDetector {
-    await_found: bool,
-}
-
-impl<'ast> AstVisitor<'ast> for AwaitDetector {
-    fn visit_expr(&mut self, ex: &'ast Expr) {
-        match (&ex.kind, self.await_found) {
-            (ExprKind::Await(_), _) => self.await_found = true,
-            (_, false) => rustc_ast::visit::walk_expr(self, ex),
-            _ => (),
-        }
+/// If `expr` is a desugared `async` block, return the original expression if it does not capture
+/// any variable by ref.
+fn desugar_async_block<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+    if let ExprKind::Closure(Closure { body, def_id, .. }) = expr.kind &&
+        let body = cx.tcx.hir().body(*body) &&
+        matches!(body.generator_kind, Some(GeneratorKind::Async(AsyncGeneratorKind::Block)))
+    {
+        cx
+            .typeck_results()
+            .closure_min_captures
+            .get(def_id)
+            .map_or(true, |m| {
+                m.values().all(|places| {
+                    places
+                        .iter()
+                        .all(|place| matches!(place.info.capture_kind, UpvarCapture::ByValue))
+                })
+            })
+            .then_some(body.value)
+    } else {
+        None
     }
 }
 
-/// Check whether an expression may have captured a local variable.
-/// This is done by looking for paths with only one segment, except as
-/// a prefix of `.await` since this would be captured by value.
-///
-/// This function will sometimes return `true` even tough there are no
-/// captures happening: at the AST level, it is impossible to
-/// dinstinguish a function call from a call to a closure which comes
-/// from the local environment.
-fn captures_value(expr: &Expr) -> bool {
-    let mut detector = CaptureDetector::default();
-    detector.visit_expr(expr);
-    detector.capture_found
-}
-
-#[derive(Default)]
-struct CaptureDetector {
-    capture_found: bool,
-}
-
-impl<'ast> AstVisitor<'ast> for CaptureDetector {
-    fn visit_expr(&mut self, ex: &'ast Expr) {
-        match (&ex.kind, self.capture_found) {
-            (ExprKind::Await(fut), _) if matches!(fut.kind, ExprKind::Path(..)) => (),
-            (ExprKind::Path(_, path), _) if path.segments.len() == 1 => self.capture_found = true,
-            (_, false) => rustc_ast::visit::walk_expr(self, ex),
-            _ => (),
-        }
+/// If `expr` is a desugared `.await`, return the original expression if it does not come from a
+/// macro expansion.
+fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+    if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind &&
+        let ExprKind::Call(_, [into_future_arg]) = match_value.kind &&
+        let ctxt = expr.span.ctxt() &&
+        for_each_expr(into_future_arg, |e|
+            walk_span_to_context(e.span, ctxt)
+                .map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))).is_none()
+    {
+        Some(into_future_arg)
+    } else {
+        None
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
index 11b908e7e53..038dfe8e480 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
-use rustc_ast::ast::{Item, ItemKind, Ty, TyKind, StaticItem, ConstItem};
+use rustc_ast::ast::{ConstItem, Item, ItemKind, StaticItem, Ty, TyKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -106,7 +106,7 @@ impl EarlyLintPass for RedundantStaticLifetimes {
                 // #2438)
             }
 
-            if let ItemKind::Static(box StaticItem { ty: ref var_type,.. }) = item.kind {
+            if let ItemKind::Static(box StaticItem { ty: ref var_type, .. }) = item.kind {
                 Self::visit_type(var_type, cx, "statics have by default a `'static` lifetime");
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index f0d7dd23a67..df126d7617e 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -9,7 +9,7 @@ use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, LangItem, MatchSource, PatKind, QPath, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::def_id::LocalDefId;
 use rustc_span::source_map::Span;
@@ -175,7 +175,7 @@ impl<'tcx> LateLintPass<'tcx> for Return {
                 } else {
                     RetReplacement::Empty
                 };
-                check_final_expr(cx, body.value, vec![], replacement);
+                check_final_expr(cx, body.value, vec![], replacement, None);
             },
             FnKind::ItemFn(..) | FnKind::Method(..) => {
                 check_block_return(cx, &body.value.kind, sp, vec![]);
@@ -188,11 +188,11 @@ impl<'tcx> LateLintPass<'tcx> for Return {
 fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, sp: Span, mut semi_spans: Vec<Span>) {
     if let ExprKind::Block(block, _) = expr_kind {
         if let Some(block_expr) = block.expr {
-            check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty);
+            check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty, None);
         } else if let Some(stmt) = block.stmts.iter().last() {
             match stmt.kind {
                 StmtKind::Expr(expr) => {
-                    check_final_expr(cx, expr, semi_spans, RetReplacement::Empty);
+                    check_final_expr(cx, expr, semi_spans, RetReplacement::Empty, None);
                 },
                 StmtKind::Semi(semi_expr) => {
                     // Remove ending semicolons and any whitespace ' ' in between.
@@ -202,7 +202,7 @@ fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>,
                             span_find_starting_semi(cx.sess().source_map(), semi_span.with_hi(sp.hi()));
                         semi_spans.push(semi_span_to_remove);
                     }
-                    check_final_expr(cx, semi_expr, semi_spans, RetReplacement::Empty);
+                    check_final_expr(cx, semi_expr, semi_spans, RetReplacement::Empty, None);
                 },
                 _ => (),
             }
@@ -216,6 +216,7 @@ fn check_final_expr<'tcx>(
     semi_spans: Vec<Span>, /* containing all the places where we would need to remove semicolons if finding an
                             * needless return */
     replacement: RetReplacement<'tcx>,
+    match_ty_opt: Option<Ty<'_>>,
 ) {
     let peeled_drop_expr = expr.peel_drop_temps();
     match &peeled_drop_expr.kind {
@@ -244,7 +245,22 @@ fn check_final_expr<'tcx>(
                     RetReplacement::Expr(snippet, applicability)
                 }
             } else {
-                replacement
+                match match_ty_opt {
+                    Some(match_ty) => {
+                        match match_ty.kind() {
+                            // If the code got till here with
+                            // tuple not getting detected before it,
+                            // then we are sure it's going to be Unit
+                            // type
+                            ty::Tuple(_) => RetReplacement::Unit,
+                            // We don't want to anything in this case
+                            // cause we can't predict what the user would
+                            // want here
+                            _ => return,
+                        }
+                    },
+                    None => replacement,
+                }
             };
 
             if !cx.tcx.hir().attrs(expr.hir_id).is_empty() {
@@ -268,8 +284,9 @@ fn check_final_expr<'tcx>(
         // note, if without else is going to be a type checking error anyways
         // (except for unit type functions) so we don't match it
         ExprKind::Match(_, arms, MatchSource::Normal) => {
+            let match_ty = cx.typeck_results().expr_ty(peeled_drop_expr);
             for arm in arms.iter() {
-                check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit);
+                check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit, Some(match_ty));
             }
         },
         // if it's a whole block, check it
@@ -293,6 +310,7 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec<Span>,
     if ret_span.from_expansion() {
         return;
     }
+
     let applicability = replacement.applicability().unwrap_or(Applicability::MachineApplicable);
     let return_replacement = replacement.to_string();
     let sugg_help = replacement.sugg_help();
diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
index d46f6a6352c..5743dd21c28 100644
--- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
@@ -1,6 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
 use rustc_ast::node_id::{NodeId, NodeMap};
-use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind};
+use rustc_ast::visit::{walk_expr, Visitor};
+use rustc_ast::{ptr::P, Crate, Expr, ExprKind, Item, ItemKind, MacroDef, ModKind, Ty, TyKind, UseTreeKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -55,7 +56,7 @@ impl EarlyLintPass for SingleComponentPathImports {
             return;
         }
 
-        self.check_mod(cx, &krate.items);
+        self.check_mod(&krate.items);
     }
 
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
@@ -84,8 +85,43 @@ impl EarlyLintPass for SingleComponentPathImports {
     }
 }
 
+#[derive(Default)]
+struct ImportUsageVisitor {
+    // keep track of imports reused with `self` keyword, such as `self::std` in the example below.
+    // Removing the `use std;` would make this a compile error (#10549)
+    // ```
+    // use std;
+    //
+    // fn main() {
+    //     let _ = self::std::io::stdout();
+    // }
+    // ```
+    imports_referenced_with_self: Vec<Symbol>,
+}
+
+impl<'tcx> Visitor<'tcx> for ImportUsageVisitor {
+    fn visit_expr(&mut self, expr: &Expr) {
+        if let ExprKind::Path(_, path) = &expr.kind
+            && path.segments.len() > 1
+            && path.segments[0].ident.name == kw::SelfLower
+        {
+            self.imports_referenced_with_self.push(path.segments[1].ident.name);
+        }
+        walk_expr(self, expr);
+    }
+
+    fn visit_ty(&mut self, ty: &Ty) {
+        if let TyKind::Path(_, path) = &ty.kind
+            && path.segments.len() > 1
+            && path.segments[0].ident.name == kw::SelfLower
+        {
+            self.imports_referenced_with_self.push(path.segments[1].ident.name);
+        }
+    }
+}
+
 impl SingleComponentPathImports {
-    fn check_mod(&mut self, cx: &EarlyContext<'_>, items: &[P<Item>]) {
+    fn check_mod(&mut self, items: &[P<Item>]) {
         // keep track of imports reused with `self` keyword, such as `self::crypto_hash` in the example
         // below. Removing the `use crypto_hash;` would make this a compile error
         // ```
@@ -108,18 +144,16 @@ impl SingleComponentPathImports {
         // ```
         let mut macros = Vec::new();
 
+        let mut import_usage_visitor = ImportUsageVisitor::default();
         for item in items {
-            self.track_uses(
-                cx,
-                item,
-                &mut imports_reused_with_self,
-                &mut single_use_usages,
-                &mut macros,
-            );
+            self.track_uses(item, &mut imports_reused_with_self, &mut single_use_usages, &mut macros);
+            import_usage_visitor.visit_item(item);
         }
 
         for usage in single_use_usages {
-            if !imports_reused_with_self.contains(&usage.name) {
+            if !imports_reused_with_self.contains(&usage.name)
+                && !import_usage_visitor.imports_referenced_with_self.contains(&usage.name)
+            {
                 self.found.entry(usage.item_id).or_default().push(usage);
             }
         }
@@ -127,7 +161,6 @@ impl SingleComponentPathImports {
 
     fn track_uses(
         &mut self,
-        cx: &EarlyContext<'_>,
         item: &Item,
         imports_reused_with_self: &mut Vec<Symbol>,
         single_use_usages: &mut Vec<SingleUse>,
@@ -139,7 +172,7 @@ impl SingleComponentPathImports {
 
         match &item.kind {
             ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => {
-                self.check_mod(cx, items);
+                self.check_mod(items);
             },
             ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => {
                 macros.push(item.ident.name);
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs b/src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs
new file mode 100644
index 00000000000..e5746ca99ca
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs
@@ -0,0 +1,94 @@
+use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then};
+use if_chain::if_chain;
+use rustc_ast::{token::CommentKind, AttrKind, AttrStyle, Attribute, Item};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Detects the use of outer doc comments (`///`, `/**`) followed by a bang (`!`): `///!`
+    ///
+    /// ### Why is this bad?
+    /// Triple-slash comments (known as "outer doc comments") apply to items that follow it.
+    /// An outer doc comment followed by a bang (i.e. `///!`) has no specific meaning.
+    ///
+    /// The user most likely meant to write an inner doc comment (`//!`, `/*!`), which
+    /// applies to the parent item (i.e. the item that the comment is contained in,
+    /// usually a module or crate).
+    ///
+    /// ### Known problems
+    /// Inner doc comments can only appear before items, so there are certain cases where the suggestion
+    /// made by this lint is not valid code. For example:
+    /// ```rs
+    /// fn foo() {}
+    /// ///!
+    /// fn bar() {}
+    /// ```
+    /// This lint detects the doc comment and suggests changing it to `//!`, but an inner doc comment
+    /// is not valid at that position.
+    ///
+    /// ### Example
+    /// In this example, the doc comment is attached to the *function*, rather than the *module*.
+    /// ```rust
+    /// pub mod util {
+    ///     ///! This module contains utility functions.
+    ///
+    ///     pub fn dummy() {}
+    /// }
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// pub mod util {
+    ///     //! This module contains utility functions.
+    ///
+    ///     pub fn dummy() {}
+    /// }
+    /// ```
+    #[clippy::version = "1.70.0"]
+    pub SUSPICIOUS_DOC_COMMENTS,
+    suspicious,
+    "suspicious usage of (outer) doc comments"
+}
+declare_lint_pass!(SuspiciousDocComments => [SUSPICIOUS_DOC_COMMENTS]);
+
+const WARNING: &str = "this is an outer doc comment and does not apply to the parent module or crate";
+const HELP: &str = "use an inner doc comment to document the parent module or crate";
+
+impl EarlyLintPass for SuspiciousDocComments {
+    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+        let replacements = collect_doc_comment_replacements(&item.attrs);
+
+        if let Some(((lo_span, _), (hi_span, _))) = replacements.first().zip(replacements.last()) {
+            let span = lo_span.to(*hi_span);
+
+            span_lint_and_then(cx, SUSPICIOUS_DOC_COMMENTS, span, WARNING, |diag| {
+                multispan_sugg_with_applicability(diag, HELP, Applicability::MaybeIncorrect, replacements);
+            });
+        }
+    }
+}
+
+fn collect_doc_comment_replacements(attrs: &[Attribute]) -> Vec<(Span, String)> {
+    attrs
+        .iter()
+        .filter_map(|attr| {
+            if_chain! {
+                if let AttrKind::DocComment(com_kind, sym) = attr.kind;
+                if let AttrStyle::Outer = attr.style;
+                if let Some(com) = sym.as_str().strip_prefix('!');
+                then {
+                    let sugg = match com_kind {
+                        CommentKind::Line => format!("//!{com}"),
+                        CommentKind::Block => format!("/*!{com}*/")
+                    };
+                    Some((attr.span, sugg))
+                } else {
+                    None
+                }
+            }
+        })
+        .collect()
+}
diff --git a/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
new file mode 100644
index 00000000000..0a0a77082e0
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
@@ -0,0 +1,71 @@
+use clippy_utils::{diagnostics::span_lint_and_note, is_in_cfg_test, is_in_test_function};
+use rustc_hir::{intravisit::FnKind, Body, FnDecl};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{def_id::LocalDefId, Span};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Triggers when a testing function (marked with the `#[test]` attribute) isn't inside a testing module
+    /// (marked with `#[cfg(test)]`).
+    /// ### Why is this bad?
+    /// The idiomatic (and more performant) way of writing tests is inside a testing module (flagged with `#[cfg(test)]`),
+    /// having test functions outside of this module is confusing and may lead to them being "hidden".
+    /// ### Example
+    /// ```rust
+    /// #[test]
+    /// fn my_cool_test() {
+    ///     // [...]
+    /// }
+    ///
+    /// #[cfg(test)]
+    /// mod tests {
+    ///     // [...]
+    /// }
+    ///
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// #[cfg(test)]
+    /// mod tests {
+    ///     #[test]
+    ///     fn my_cool_test() {
+    ///         // [...]
+    ///     }
+    /// }
+    /// ```
+    #[clippy::version = "1.70.0"]
+    pub TESTS_OUTSIDE_TEST_MODULE,
+    restriction,
+    "A test function is outside the testing module."
+}
+
+declare_lint_pass!(TestsOutsideTestModule => [TESTS_OUTSIDE_TEST_MODULE]);
+
+impl LateLintPass<'_> for TestsOutsideTestModule {
+    fn check_fn(
+        &mut self,
+        cx: &LateContext<'_>,
+        kind: FnKind<'_>,
+        _: &FnDecl<'_>,
+        body: &Body<'_>,
+        sp: Span,
+        _: LocalDefId,
+    ) {
+        if_chain! {
+            if !matches!(kind, FnKind::Closure);
+            if is_in_test_function(cx.tcx, body.id().hir_id);
+            if !is_in_cfg_test(cx.tcx, body.id().hir_id);
+            then {
+                span_lint_and_note(
+                    cx,
+                    TESTS_OUTSIDE_TEST_MODULE,
+                    sp,
+                    "this function marked with #[test] is outside a #[cfg(test)] module",
+                    None,
+                    "move it to a testing module marked with #[cfg(test)]",
+                );
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
index 8530b43243f..85cd74f23ef 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
@@ -2,8 +2,9 @@ use super::utils::check_cast;
 use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::sugg::Sugg;
+use rustc_ast::ExprPrecedence;
 use rustc_errors::Applicability;
-use rustc_hir::Expr;
+use rustc_hir::{Expr, Node};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{cast::CastKind, Ty};
 
@@ -19,7 +20,7 @@ pub(super) fn check<'tcx>(
 ) -> bool {
     use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast};
     let mut app = Applicability::MachineApplicable;
-    let sugg = match check_cast(cx, e, from_ty, to_ty) {
+    let mut sugg = match check_cast(cx, e, from_ty, to_ty) {
         Some(PtrPtrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) => {
             Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app)
                 .as_ty(to_ty.to_string())
@@ -39,6 +40,12 @@ pub(super) fn check<'tcx>(
         _ => return false,
     };
 
+    if let Node::Expr(parent) = cx.tcx.hir().get_parent(e.hir_id)
+        && parent.precedence().order() > ExprPrecedence::Cast.order()
+    {
+        sugg = format!("({sugg})");
+    }
+
     span_lint_and_sugg(
         cx,
         TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
new file mode 100644
index 00000000000..912bcda630b
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
@@ -0,0 +1,120 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_errors::Applicability;
+use rustc_hir::{def_id::LocalDefId, FnDecl, FnRetTy, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::Symbol;
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Checks for a return type containing a `Box<T>` where `T` implements `Sized`
+    ///
+    /// ### Why is this bad?
+    ///
+    /// It's better to just return `T` in these cases. The caller may not need
+    /// the value to be boxed, and it's expensive to free the memory once the
+    /// `Box<T>` been dropped.
+    ///
+    /// ### Example
+    /// ```rust
+    /// fn foo() -> Box<String> {
+    ///     Box::new(String::from("Hello, world!"))
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// fn foo() -> String {
+    ///     String::from("Hello, world!")
+    /// }
+    /// ```
+    #[clippy::version = "1.70.0"]
+    pub UNNECESSARY_BOX_RETURNS,
+    pedantic,
+    "Needlessly returning a Box"
+}
+
+pub struct UnnecessaryBoxReturns {
+    avoid_breaking_exported_api: bool,
+}
+
+impl_lint_pass!(UnnecessaryBoxReturns => [UNNECESSARY_BOX_RETURNS]);
+
+impl UnnecessaryBoxReturns {
+    pub fn new(avoid_breaking_exported_api: bool) -> Self {
+        Self {
+            avoid_breaking_exported_api,
+        }
+    }
+
+    fn check_fn_item(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, def_id: LocalDefId, name: Symbol) {
+        // we don't want to tell someone to break an exported function if they ask us not to
+        if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) {
+            return;
+        }
+
+        // functions which contain the word "box" are exempt from this lint
+        if name.as_str().contains("box") {
+            return;
+        }
+
+        let FnRetTy::Return(return_ty_hir) = &decl.output else { return };
+
+        let return_ty = cx
+            .tcx
+            .erase_late_bound_regions(cx.tcx.fn_sig(def_id).skip_binder())
+            .output();
+
+        if !return_ty.is_box() {
+            return;
+        }
+
+        let boxed_ty = return_ty.boxed_ty();
+
+        // it's sometimes useful to return Box<T> if T is unsized, so don't lint those
+        if boxed_ty.is_sized(cx.tcx, cx.param_env) {
+            span_lint_and_then(
+                cx,
+                UNNECESSARY_BOX_RETURNS,
+                return_ty_hir.span,
+                format!("boxed return of the sized type `{boxed_ty}`").as_str(),
+                |diagnostic| {
+                    diagnostic.span_suggestion(
+                        return_ty_hir.span,
+                        "try",
+                        boxed_ty.to_string(),
+                        // the return value and function callers also needs to
+                        // be changed, so this can't be MachineApplicable
+                        Applicability::Unspecified,
+                    );
+                    diagnostic.help("changing this also requires a change to the return expressions in this function");
+                },
+            );
+        }
+    }
+}
+
+impl LateLintPass<'_> for UnnecessaryBoxReturns {
+    fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
+        let TraitItemKind::Fn(signature, _) = &item.kind else { return };
+        self.check_fn_item(cx, signature.decl, item.owner_id.def_id, item.ident.name);
+    }
+
+    fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<'_>) {
+        // Ignore implementations of traits, because the lint should be on the
+        // trait, not on the implmentation of it.
+        let Node::Item(parent) = cx.tcx.hir().get_parent(item.hir_id()) else { return };
+        let ItemKind::Impl(parent) = parent.kind else { return };
+        if parent.of_trait.is_some() {
+            return;
+        }
+
+        let ImplItemKind::Fn(signature, ..) = &item.kind else { return };
+        self.check_fn_item(cx, signature.decl, item.owner_id.def_id, item.ident.name);
+    }
+
+    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
+        let ItemKind::Fn(signature, ..) = &item.kind else { return };
+        self.check_fn_item(cx, signature.decl, item.owner_id.def_id, item.ident.name);
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
index af0b4b1592f..084b031982d 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
@@ -9,7 +9,7 @@ declare_clippy_lint! {
     /// any field.
     ///
     /// ### Why is this bad?
-    /// Readibility suffers from unnecessary struct building.
+    /// Readability suffers from unnecessary struct building.
     ///
     /// ### Example
     /// ```rust
@@ -25,9 +25,13 @@ declare_clippy_lint! {
     /// let a = S { s: String::from("Hello, world!") };
     /// let b = a;
     /// ```
+    ///
+    /// ### Known Problems
+    /// Has false positives when the base is a place expression that cannot be
+    /// moved out of, see [#10547](https://github.com/rust-lang/rust-clippy/issues/10547).
     #[clippy::version = "1.70.0"]
     pub UNNECESSARY_STRUCT_INITIALIZATION,
-    complexity,
+    nursery,
     "struct built from a base that can be written mode concisely"
 }
 declare_lint_pass!(UnnecessaryStruct => [UNNECESSARY_STRUCT_INITIALIZATION]);
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index 7dfb0956077..5a02987453c 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -10,8 +10,8 @@ use rustc_hir::{
     def::{CtorOf, DefKind, Res},
     def_id::LocalDefId,
     intravisit::{walk_inf, walk_ty, Visitor},
-    Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericArgsParentheses, GenericParam, GenericParamKind, HirId, Impl, ImplItemKind, Item,
-    ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind,
+    Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericArgsParentheses, GenericParam, GenericParamKind, HirId, Impl,
+    ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind,
 };
 use rustc_hir_analysis::hir_ty_to_ty;
 use rustc_lint::{LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs
index 8ba252425a3..896a01af37d 100644
--- a/src/tools/clippy/clippy_lints/src/utils/conf.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs
@@ -249,7 +249,7 @@ define_Conf! {
     /// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"]
     /// ```
     (arithmetic_side_effects_allowed_unary: rustc_data_structures::fx::FxHashSet<String> = <_>::default()),
-    /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
+    /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX, UNNECESSARY_BOX_RETURNS.
     ///
     /// Suppress lints whenever the suggested change would cause breakage for other crates.
     (avoid_breaking_exported_api: bool = true),
@@ -275,13 +275,13 @@ define_Conf! {
     ///
     /// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value
     /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the
-    /// default configuration of Clippy. By default any configuration will replace the default value.
+    /// default configuration of Clippy. By default, any configuration will replace the default value.
     (disallowed_names: Vec<String> = super::DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect()),
     /// Lint: DOC_MARKDOWN.
     ///
     /// The list of words this lint should not consider as identifiers needing ticks. The value
     /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the
-    /// default configuration of Clippy. By default any configuraction will replace the default value. For example:
+    /// default configuration of Clippy. By default, any configuration will replace the default value. For example:
     /// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
     /// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
     ///
@@ -390,7 +390,7 @@ define_Conf! {
     /// Enforce the named macros always use the braces specified.
     ///
     /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro
-    /// is could be used with a full path two `MacroMatcher`s have to be added one with the full path
+    /// could be used with a full path two `MacroMatcher`s have to be added one with the full path
     /// `crate_name::macro_name` and one with just the macro name.
     (standard_macro_braces: Vec<crate::nonstandard_macro_braces::MacroMatcher> = Vec::new()),
     /// Lint: MISSING_ENFORCED_IMPORT_RENAMES.
@@ -408,7 +408,7 @@ define_Conf! {
     /// Lint: INDEX_REFUTABLE_SLICE.
     ///
     /// When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in
-    /// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed.
+    /// the slice pattern that is suggested. If more elements are necessary, the lint is suppressed.
     /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
     (max_suggested_slice_pattern_length: u64 = 3),
     /// Lint: AWAIT_HOLDING_INVALID_TYPE.
@@ -459,6 +459,10 @@ define_Conf! {
     /// Whether to **only** check for missing documentation in items visible within the current
     /// crate. For example, `pub(crate)` items.
     (missing_docs_in_crate_items: bool = false),
+    /// Lint: LARGE_FUTURES.
+    ///
+    /// The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint
+    (future_size_threshold: u64 = 16 * 1024),
 }
 
 /// Search for the configuration file.
@@ -466,7 +470,7 @@ define_Conf! {
 /// # Errors
 ///
 /// Returns any unexpected filesystem error encountered when searching for the config file
-pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
+pub fn lookup_conf_file() -> io::Result<(Option<PathBuf>, Vec<String>)> {
     /// Possible filename to search for.
     const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
 
@@ -474,9 +478,11 @@ pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
     // If neither of those exist, use ".".
     let mut current = env::var_os("CLIPPY_CONF_DIR")
         .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
-        .map_or_else(|| PathBuf::from("."), PathBuf::from);
+        .map_or_else(|| PathBuf::from("."), PathBuf::from)
+        .canonicalize()?;
 
     let mut found_config: Option<PathBuf> = None;
+    let mut warnings = vec![];
 
     loop {
         for config_file_name in &CONFIG_FILE_NAMES {
@@ -487,12 +493,12 @@ pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
                     Ok(md) if md.is_dir() => {},
                     Ok(_) => {
                         // warn if we happen to find two config files #8323
-                        if let Some(ref found_config_) = found_config {
-                            eprintln!(
-                                "Using config file `{}`\nWarning: `{}` will be ignored.",
-                                found_config_.display(),
-                                config_file.display(),
-                            );
+                        if let Some(ref found_config) = found_config {
+                            warnings.push(format!(
+                                "using config file `{}`, `{}` will be ignored",
+                                found_config.display(),
+                                config_file.display()
+                            ));
                         } else {
                             found_config = Some(config_file);
                         }
@@ -502,12 +508,12 @@ pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
         }
 
         if found_config.is_some() {
-            return Ok(found_config);
+            return Ok((found_config, warnings));
         }
 
         // If the current directory has no parent, we're done searching.
         if !current.pop() {
-            return Ok(None);
+            return Ok((None, warnings));
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
index be56b842b98..09fcb82c37c 100644
--- a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
@@ -1,7 +1,12 @@
 use clippy_utils::macros::collect_ast_format_args;
-use rustc_ast::{Expr, ExprKind};
+use clippy_utils::source::snippet_opt;
+use itertools::Itertools;
+use rustc_ast::{Expr, ExprKind, FormatArgs};
+use rustc_lexer::{tokenize, TokenKind};
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::hygiene;
+use std::iter::once;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -15,9 +20,79 @@ declare_clippy_lint! {
 declare_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]);
 
 impl EarlyLintPass for FormatArgsCollector {
-    fn check_expr(&mut self, _: &EarlyContext<'_>, expr: &Expr) {
+    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
         if let ExprKind::FormatArgs(args) = &expr.kind {
+            if has_span_from_proc_macro(cx, args) {
+                return;
+            }
+
             collect_ast_format_args(expr.span, args);
         }
     }
 }
+
+/// Detects if the format string or an argument has its span set by a proc macro to something inside
+/// a macro callsite, e.g.
+///
+/// ```ignore
+/// println!(some_proc_macro!("input {}"), a);
+/// ```
+///
+/// Where `some_proc_macro` expands to
+///
+/// ```ignore
+/// println!("output {}", a);
+/// ```
+///
+/// But with the span of `"output {}"` set to the macro input
+///
+/// ```ignore
+/// println!(some_proc_macro!("input {}"), a);
+/// //                        ^^^^^^^^^^
+/// ```
+fn has_span_from_proc_macro(cx: &EarlyContext<'_>, args: &FormatArgs) -> bool {
+    let ctxt = args.span.ctxt();
+
+    // `format!("{} {} {c}", "one", "two", c = "three")`
+    //                       ^^^^^  ^^^^^      ^^^^^^^
+    let argument_span = args
+        .arguments
+        .explicit_args()
+        .iter()
+        .map(|argument| hygiene::walk_chain(argument.expr.span, ctxt));
+
+    // `format!("{} {} {c}", "one", "two", c = "three")`
+    //                     ^^     ^^     ^^^^^^
+    let between_spans = once(args.span)
+        .chain(argument_span)
+        .tuple_windows()
+        .map(|(start, end)| start.between(end));
+
+    for between_span in between_spans {
+        let mut seen_comma = false;
+
+        let Some(snippet) = snippet_opt(cx, between_span) else { return true };
+        for token in tokenize(&snippet) {
+            match token.kind {
+                TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace => {},
+                TokenKind::Comma if !seen_comma => seen_comma = true,
+                // named arguments, `start_val, name = end_val`
+                //                            ^^^^^^^^^ between_span
+                TokenKind::Ident | TokenKind::Eq if seen_comma => {},
+                // An unexpected token usually indicates that we crossed a macro boundary
+                //
+                // `println!(some_proc_macro!("input {}"), a)`
+                //                                      ^^^ between_span
+                // `println!("{}", val!(x))`
+                //               ^^^^^^^ between_span
+                _ => return true,
+            }
+        }
+
+        if !seen_comma {
+            return true;
+        }
+    }
+
+    false
+}
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index 8114a8463fa..d7c94b909bd 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -463,12 +463,18 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
             && let Some(value_string) = snippet_opt(cx, arg.expr.span)
     {
             let (replacement, replace_raw) = match lit.kind {
-                LitKind::Str | LitKind::StrRaw(_)  => extract_str_literal(&value_string),
+                LitKind::Str | LitKind::StrRaw(_)  => match extract_str_literal(&value_string) {
+                    Some(extracted) => extracted,
+                    None => return,
+                },
                 LitKind::Char => (
                     match lit.symbol.as_str() {
                         "\"" => "\\\"",
                         "\\'" => "'",
-                        _ => &value_string[1..value_string.len() - 1],
+                        _ => match value_string.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) {
+                            Some(stripped) => stripped,
+                            None => return,
+                        },
                     }
                     .to_string(),
                     false,
@@ -533,13 +539,13 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
 /// `r#"a"#` -> (`a`, true)
 ///
 /// `"b"` -> (`b`, false)
-fn extract_str_literal(literal: &str) -> (String, bool) {
+fn extract_str_literal(literal: &str) -> Option<(String, bool)> {
     let (literal, raw) = match literal.strip_prefix('r') {
         Some(stripped) => (stripped.trim_matches('#'), true),
         None => (literal, false),
     };
 
-    (literal[1..literal.len() - 1].to_string(), raw)
+    Some((literal.strip_prefix('"')?.strip_suffix('"')?.to_string(), raw))
 }
 
 enum UnescapeErr {