about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/attrs.rs20
-rw-r--r--clippy_lints/src/booleans.rs8
-rw-r--r--clippy_lints/src/escape.rs5
-rw-r--r--clippy_lints/src/implicit_return.rs39
-rw-r--r--clippy_lints/src/macro_use.rs42
-rw-r--r--clippy_lints/src/manual_non_exhaustive.rs9
-rw-r--r--clippy_lints/src/ptr.rs41
-rw-r--r--clippy_lints/src/returns.rs71
-rw-r--r--clippy_lints/src/unwrap.rs8
-rw-r--r--clippy_utils/src/lib.rs8
-rw-r--r--tests/ui/boxed_local.rs (renamed from tests/ui/escape_analysis.rs)5
-rw-r--r--tests/ui/boxed_local.stderr (renamed from tests/ui/escape_analysis.stderr)8
-rw-r--r--tests/ui/checked_unwrap/simple_conditionals.rs16
-rw-r--r--tests/ui/checked_unwrap/simple_conditionals.stderr38
-rw-r--r--tests/ui/implicit_return.fixed12
-rw-r--r--tests/ui/implicit_return.rs12
-rw-r--r--tests/ui/logic_bug.rs8
-rw-r--r--tests/ui/logic_bug.stderr20
-rw-r--r--tests/ui/macro_use_imports.fixed1
-rw-r--r--tests/ui/macro_use_imports.rs1
-rw-r--r--tests/ui/macro_use_imports.stderr14
-rw-r--r--tests/ui/macro_use_imports_expect.rs51
-rw-r--r--tests/ui/manual_non_exhaustive_enum.rs9
-rw-r--r--tests/ui/manual_non_exhaustive_enum.stderr8
-rw-r--r--tests/ui/needless_return.fixed10
-rw-r--r--tests/ui/needless_return.rs10
-rw-r--r--tests/ui/needless_return.stderr74
-rw-r--r--tests/ui/nonminimal_bool.rs7
-rw-r--r--tests/ui/nonminimal_bool.stderr24
-rw-r--r--tests/ui/ptr_arg.rs6
-rw-r--r--tests/ui/ptr_arg.stderr40
-rw-r--r--tests/ui/redundant_clone.fixed5
-rw-r--r--tests/ui/redundant_clone.rs5
-rw-r--r--tests/ui/redundant_clone.stderr60
34 files changed, 464 insertions, 231 deletions
diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs
index ed12ad9c367..4bcbeacf9fe 100644
--- a/clippy_lints/src/attrs.rs
+++ b/clippy_lints/src/attrs.rs
@@ -78,10 +78,17 @@ declare_clippy_lint! {
     /// Checks for `extern crate` and `use` items annotated with
     /// lint attributes.
     ///
-    /// This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`,
-    /// `#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and
-    /// `#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on
-    /// `extern crate` items with a `#[macro_use]` attribute.
+    /// This lint permits lint attributes for lints emitted on the items themself.
+    /// For `use` items these lints are:
+    /// * deprecated
+    /// * unreachable_pub
+    /// * unused_imports
+    /// * clippy::enum_glob_use
+    /// * clippy::macro_use_imports
+    /// * clippy::wildcard_imports
+    ///
+    /// For `extern crate` items these lints are:
+    /// * `unused_imports` on items with `#[macro_use]`
     ///
     /// ### Why is this bad?
     /// Lint attributes have no effect on crate imports. Most
@@ -347,7 +354,10 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
                                             || extract_clippy_lint(lint).map_or(false, |s| {
                                                 matches!(
                                                     s.as_str(),
-                                                    "wildcard_imports" | "enum_glob_use" | "redundant_pub_crate",
+                                                    "wildcard_imports"
+                                                        | "enum_glob_use"
+                                                        | "redundant_pub_crate"
+                                                        | "macro_use_imports",
                                                 )
                                             })
                                         {
diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs
index e4e122ba6eb..526ee2f891a 100644
--- a/clippy_lints/src/booleans.rs
+++ b/clippy_lints/src/booleans.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 use clippy_utils::{eq_expr_value, get_trait_def_id, paths};
@@ -394,9 +394,10 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
                         continue 'simplified;
                     }
                     if stats.terminals[i] != 0 && simplified_stats.terminals[i] == 0 {
-                        span_lint_and_then(
+                        span_lint_hir_and_then(
                             self.cx,
                             LOGIC_BUG,
+                            e.hir_id,
                             e.span,
                             "this boolean expression contains a logic bug",
                             |diag| {
@@ -429,9 +430,10 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
                 }
             }
             let nonminimal_bool_lint = |suggestions: Vec<_>| {
-                span_lint_and_then(
+                span_lint_hir_and_then(
                     self.cx,
                     NONMINIMAL_BOOL,
+                    e.hir_id,
                     e.span,
                     "this boolean expression can be simplified",
                     |diag| {
diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs
index 9d21dd71e0e..7a65b849a66 100644
--- a/clippy_lints/src/escape.rs
+++ b/clippy_lints/src/escape.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint;
+use clippy_utils::diagnostics::span_lint_hir;
 use clippy_utils::ty::contains_ty;
 use rustc_hir::intravisit;
 use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node};
@@ -118,9 +118,10 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
         });
 
         for node in v.set {
-            span_lint(
+            span_lint_hir(
                 cx,
                 BOXED_LOCAL,
+                node,
                 cx.tcx.hir().span(node),
                 "local variable doesn't need to be boxed here",
             );
diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs
index 647947d5d30..a6610ade37e 100644
--- a/clippy_lints/src/implicit_return.rs
+++ b/clippy_lints/src/implicit_return.rs
@@ -1,5 +1,5 @@
 use clippy_utils::{
-    diagnostics::span_lint_and_sugg,
+    diagnostics::span_lint_hir_and_then,
     get_async_fn_body, is_async_fn,
     source::{snippet_with_applicability, snippet_with_context, walk_span_to_context},
     visitors::expr_visitor_no_bodies,
@@ -43,31 +43,38 @@ declare_clippy_lint! {
 
 declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]);
 
-fn lint_return(cx: &LateContext<'_>, span: Span) {
+fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) {
     let mut app = Applicability::MachineApplicable;
     let snip = snippet_with_applicability(cx, span, "..", &mut app);
-    span_lint_and_sugg(
+    span_lint_hir_and_then(
         cx,
         IMPLICIT_RETURN,
+        emission_place,
         span,
         "missing `return` statement",
-        "add `return` as shown",
-        format!("return {}", snip),
-        app,
+        |diag| {
+            diag.span_suggestion(span, "add `return` as shown", format!("return {}", snip), app);
+        },
     );
 }
 
-fn lint_break(cx: &LateContext<'_>, break_span: Span, expr_span: Span) {
+fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, expr_span: Span) {
     let mut app = Applicability::MachineApplicable;
     let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0;
-    span_lint_and_sugg(
+    span_lint_hir_and_then(
         cx,
         IMPLICIT_RETURN,
+        emission_place,
         break_span,
         "missing `return` statement",
-        "change `break` to `return` as shown",
-        format!("return {}", snip),
-        app,
+        |diag| {
+            diag.span_suggestion(
+                break_span,
+                "change `break` to `return` as shown",
+                format!("return {}", snip),
+                app,
+            );
+        },
     );
 }
 
@@ -152,7 +159,7 @@ fn lint_implicit_returns(
                             // At this point sub_expr can be `None` in async functions which either diverge, or return
                             // the unit type.
                             if let Some(sub_expr) = sub_expr {
-                                lint_break(cx, e.span, sub_expr.span);
+                                lint_break(cx, e.hir_id, e.span, sub_expr.span);
                             }
                         } else {
                             // the break expression is from a macro call, add a return to the loop
@@ -166,10 +173,10 @@ fn lint_implicit_returns(
             if add_return {
                 #[expect(clippy::option_if_let_else)]
                 if let Some(span) = call_site_span {
-                    lint_return(cx, span);
+                    lint_return(cx, expr.hir_id, span);
                     LintLocation::Parent
                 } else {
-                    lint_return(cx, expr.span);
+                    lint_return(cx, expr.hir_id, expr.span);
                     LintLocation::Inner
                 }
             } else {
@@ -198,10 +205,10 @@ fn lint_implicit_returns(
         {
             #[expect(clippy::option_if_let_else)]
             if let Some(span) = call_site_span {
-                lint_return(cx, span);
+                lint_return(cx, expr.hir_id, span);
                 LintLocation::Parent
             } else {
-                lint_return(cx, expr.span);
+                lint_return(cx, expr.hir_id, expr.span);
                 LintLocation::Inner
             }
         },
diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs
index da806918be0..6b78c162065 100644
--- a/clippy_lints/src/macro_use.rs
+++ b/clippy_lints/src/macro_use.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::source::snippet;
 use hir::def::{DefKind, Res};
 use if_chain::if_chain;
@@ -51,8 +51,9 @@ impl MacroRefData {
 #[derive(Default)]
 #[expect(clippy::module_name_repetitions)]
 pub struct MacroUseImports {
-    /// the actual import path used and the span of the attribute above it.
-    imports: Vec<(String, Span)>,
+    /// the actual import path used and the span of the attribute above it. The value is
+    /// the location, where the lint should be emitted.
+    imports: Vec<(String, Span, hir::HirId)>,
     /// the span of the macro reference, kept to ensure only one reference is used per macro call.
     collected: FxHashSet<Span>,
     mac_refs: Vec<MacroRefData>,
@@ -91,7 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
         if_chain! {
             if cx.sess().opts.edition >= Edition::Edition2018;
             if let hir::ItemKind::Use(path, _kind) = &item.kind;
-            let attrs = cx.tcx.hir().attrs(item.hir_id());
+            let hir_id = item.hir_id();
+            let attrs = cx.tcx.hir().attrs(hir_id);
             if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use));
             if let Res::Def(DefKind::Mod, id) = path.res;
             if !id.is_local();
@@ -100,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
                     if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
                         let span = mac_attr.span;
                         let def_path = cx.tcx.def_path_str(mac_id);
-                        self.imports.push((def_path, span));
+                        self.imports.push((def_path, span, hir_id));
                     }
                 }
             } else {
@@ -138,7 +140,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
     fn check_crate_post(&mut self, cx: &LateContext<'_>) {
         let mut used = FxHashMap::default();
         let mut check_dup = vec![];
-        for (import, span) in &self.imports {
+        for (import, span, hir_id) in &self.imports {
             let found_idx = self.mac_refs.iter().position(|mac| import.ends_with(&mac.name));
 
             if let Some(idx) = found_idx {
@@ -151,7 +153,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
                     [] | [_] => return,
                     [root, item] => {
                         if !check_dup.contains(&(*item).to_string()) {
-                            used.entry(((*root).to_string(), span))
+                            used.entry(((*root).to_string(), span, hir_id))
                                 .or_insert_with(Vec::new)
                                 .push((*item).to_string());
                             check_dup.push((*item).to_string());
@@ -169,13 +171,13 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
                                     }
                                 })
                                 .collect::<Vec<_>>();
-                            used.entry(((*root).to_string(), span))
+                            used.entry(((*root).to_string(), span, hir_id))
                                 .or_insert_with(Vec::new)
                                 .push(filtered.join("::"));
                             check_dup.extend(filtered);
                         } else {
                             let rest = rest.to_vec();
-                            used.entry(((*root).to_string(), span))
+                            used.entry(((*root).to_string(), span, hir_id))
                                 .or_insert_with(Vec::new)
                                 .push(rest.join("::"));
                             check_dup.extend(rest.iter().map(ToString::to_string));
@@ -186,27 +188,33 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
         }
 
         let mut suggestions = vec![];
-        for ((root, span), path) in used {
+        for ((root, span, hir_id), path) in used {
             if path.len() == 1 {
-                suggestions.push((span, format!("{}::{}", root, path[0])));
+                suggestions.push((span, format!("{}::{}", root, path[0]), hir_id));
             } else {
-                suggestions.push((span, format!("{}::{{{}}}", root, path.join(", "))));
+                suggestions.push((span, format!("{}::{{{}}}", root, path.join(", ")), hir_id));
             }
         }
 
         // If mac_refs is not empty we have encountered an import we could not handle
         // such as `std::prelude::v1::foo` or some other macro that expands to an import.
         if self.mac_refs.is_empty() {
-            for (span, import) in suggestions {
+            for (span, import, hir_id) in suggestions {
                 let help = format!("use {};", import);
-                span_lint_and_sugg(
+                span_lint_hir_and_then(
                     cx,
                     MACRO_USE_IMPORTS,
+                    *hir_id,
                     *span,
                     "`macro_use` attributes are no longer needed in the Rust 2018 edition",
-                    "remove the attribute and import the macro directly, try",
-                    help,
-                    Applicability::MaybeIncorrect,
+                    |diag| {
+                        diag.span_suggestion(
+                            *span,
+                            "remove the attribute and import the macro directly, try",
+                            help,
+                            Applicability::MaybeIncorrect,
+                        );
+                    },
                 );
             }
         }
diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs
index 14f5faafd7c..4278e98dc91 100644
--- a/clippy_lints/src/manual_non_exhaustive.rs
+++ b/clippy_lints/src/manual_non_exhaustive.rs
@@ -1,6 +1,6 @@
-use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::source::snippet_opt;
-use clippy_utils::{is_doc_hidden, is_lint_allowed, meets_msrv, msrvs};
+use clippy_utils::{is_doc_hidden, meets_msrv, msrvs};
 use rustc_ast::ast::{self, VisibilityKind};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -190,12 +190,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
                 !self
                     .constructed_enum_variants
                     .contains(&(enum_id.to_def_id(), variant_id.to_def_id()))
-                    && !is_lint_allowed(cx, MANUAL_NON_EXHAUSTIVE, cx.tcx.hir().local_def_id_to_hir_id(enum_id))
             })
         {
-            span_lint_and_then(
+            let hir_id = cx.tcx.hir().local_def_id_to_hir_id(enum_id);
+            span_lint_hir_and_then(
                 cx,
                 MANUAL_NON_EXHAUSTIVE,
+                hir_id,
                 enum_span,
                 "this seems like a manual implementation of the non-exhaustive pattern",
                 |diag| {
diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs
index 5678b8f6ca6..25b73918c0a 100644
--- a/clippy_lints/src/ptr.rs
+++ b/clippy_lints/src/ptr.rs
@@ -1,6 +1,6 @@
 //! Checks for usage of  `&Vec[_]` and `&String`.
 
-use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::expr_sig;
 use clippy_utils::visitors::contains_unsafe_block;
@@ -166,15 +166,14 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
             )
             .filter(|arg| arg.mutability() == Mutability::Not)
             {
-                span_lint_and_sugg(
-                    cx,
-                    PTR_ARG,
-                    arg.span,
-                    &arg.build_msg(),
-                    "change this to",
-                    format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)),
-                    Applicability::Unspecified,
-                );
+                span_lint_hir_and_then(cx, PTR_ARG, arg.emission_id, arg.span, &arg.build_msg(), |diag| {
+                    diag.span_suggestion(
+                        arg.span,
+                        "change this to",
+                        format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)),
+                        Applicability::Unspecified,
+                    );
+                });
             }
         }
     }
@@ -221,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
         let results = check_ptr_arg_usage(cx, body, &lint_args);
 
         for (result, args) in results.iter().zip(lint_args.iter()).filter(|(r, _)| !r.skip) {
-            span_lint_and_then(cx, PTR_ARG, args.span, &args.build_msg(), |diag| {
+            span_lint_hir_and_then(cx, PTR_ARG, args.emission_id, args.span, &args.build_msg(), |diag| {
                 diag.multipart_suggestion(
                     "change this to",
                     iter::once((args.span, format!("{}{}", args.ref_prefix, args.deref_ty.display(cx))))
@@ -315,6 +314,7 @@ struct PtrArgReplacement {
 
 struct PtrArg<'tcx> {
     idx: usize,
+    emission_id: hir::HirId,
     span: Span,
     ty_did: DefId,
     ty_name: Symbol,
@@ -419,10 +419,8 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                 if let [.., name] = path.segments;
                 if cx.tcx.item_name(adt.did()) == name.ident.name;
 
-                if !is_lint_allowed(cx, PTR_ARG, hir_ty.hir_id);
-                if params.get(i).map_or(true, |p| !is_lint_allowed(cx, PTR_ARG, p.hir_id));
-
                 then {
+                    let emission_id = params.get(i).map_or(hir_ty.hir_id, |param| param.hir_id);
                     let (method_renames, deref_ty) = match cx.tcx.get_diagnostic_name(adt.did()) {
                         Some(sym::Vec) => (
                             [("clone", ".to_owned()")].as_slice(),
@@ -455,14 +453,20 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                                 })
                                 .and_then(|arg| snippet_opt(cx, arg.span))
                                 .unwrap_or_else(|| substs.type_at(1).to_string());
-                            span_lint_and_sugg(
+                            span_lint_hir_and_then(
                                 cx,
                                 PTR_ARG,
+                                emission_id,
                                 hir_ty.span,
                                 "using a reference to `Cow` is not recommended",
-                                "change this to",
-                                format!("&{}{}", mutability.prefix_str(), ty_name),
-                                Applicability::Unspecified,
+                                |diag| {
+                                    diag.span_suggestion(
+                                        hir_ty.span,
+                                        "change this to",
+                                        format!("&{}{}", mutability.prefix_str(), ty_name),
+                                        Applicability::Unspecified,
+                                    );
+                                }
                             );
                             return None;
                         },
@@ -470,6 +474,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                     };
                     return Some(PtrArg {
                         idx: i,
+                        emission_id,
                         span: hir_ty.span,
                         ty_did: adt.did(),
                         ty_name: name.ident.name,
diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs
index e525eba53e2..5ae04947b82 100644
--- a/clippy_lints/src/returns.rs
+++ b/clippy_lints/src/returns.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::source::{snippet_opt, snippet_with_context};
 use clippy_utils::{fn_def_id, path_to_local_id};
 use if_chain::if_chain;
@@ -94,9 +94,10 @@ impl<'tcx> LateLintPass<'tcx> for Return {
             if !in_external_macro(cx.sess(), retexpr.span);
             if !local.span.from_expansion();
             then {
-                span_lint_and_then(
+                span_lint_hir_and_then(
                     cx,
                     LET_AND_RETURN,
+                    retexpr.hir_id,
                     retexpr.span,
                     "returning the result of a `let` binding from a block",
                     |err| {
@@ -185,6 +186,7 @@ fn check_final_expr<'tcx>(
                 if !borrows {
                     emit_return_lint(
                         cx,
+                        inner.map_or(expr.hir_id, |inner| inner.hir_id),
                         span.expect("`else return` is not possible"),
                         inner.as_ref().map(|i| i.span),
                         replacement,
@@ -220,50 +222,81 @@ fn check_final_expr<'tcx>(
     }
 }
 
-fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option<Span>, replacement: RetReplacement) {
+fn emit_return_lint(
+    cx: &LateContext<'_>,
+    emission_place: HirId,
+    ret_span: Span,
+    inner_span: Option<Span>,
+    replacement: RetReplacement,
+) {
     if ret_span.from_expansion() {
         return;
     }
     match inner_span {
         Some(inner_span) => {
             let mut applicability = Applicability::MachineApplicable;
-            span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
-                let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability);
-                diag.span_suggestion(ret_span, "remove `return`", snippet, applicability);
-            });
+            span_lint_hir_and_then(
+                cx,
+                NEEDLESS_RETURN,
+                emission_place,
+                ret_span,
+                "unneeded `return` statement",
+                |diag| {
+                    let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability);
+                    diag.span_suggestion(ret_span, "remove `return`", snippet, applicability);
+                },
+            );
         },
         None => match replacement {
             RetReplacement::Empty => {
-                span_lint_and_sugg(
+                span_lint_hir_and_then(
                     cx,
                     NEEDLESS_RETURN,
+                    emission_place,
                     ret_span,
                     "unneeded `return` statement",
-                    "remove `return`",
-                    String::new(),
-                    Applicability::MachineApplicable,
+                    |diag| {
+                        diag.span_suggestion(
+                            ret_span,
+                            "remove `return`",
+                            String::new(),
+                            Applicability::MachineApplicable,
+                        );
+                    },
                 );
             },
             RetReplacement::Block => {
-                span_lint_and_sugg(
+                span_lint_hir_and_then(
                     cx,
                     NEEDLESS_RETURN,
+                    emission_place,
                     ret_span,
                     "unneeded `return` statement",
-                    "replace `return` with an empty block",
-                    "{}".to_string(),
-                    Applicability::MachineApplicable,
+                    |diag| {
+                        diag.span_suggestion(
+                            ret_span,
+                            "replace `return` with an empty block",
+                            "{}".to_string(),
+                            Applicability::MachineApplicable,
+                        );
+                    },
                 );
             },
             RetReplacement::Unit => {
-                span_lint_and_sugg(
+                span_lint_hir_and_then(
                     cx,
                     NEEDLESS_RETURN,
+                    emission_place,
                     ret_span,
                     "unneeded `return` statement",
-                    "replace `return` with a unit value",
-                    "()".to_string(),
-                    Applicability::MachineApplicable,
+                    |diag| {
+                        diag.span_suggestion(
+                            ret_span,
+                            "replace `return` with a unit value",
+                            "()".to_string(),
+                            Applicability::MachineApplicable,
+                        );
+                    },
                 );
             },
         },
diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs
index 9b9e25326f9..d3f9e5abfd7 100644
--- a/clippy_lints/src/unwrap.rs
+++ b/clippy_lints/src/unwrap.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::higher;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{path_to_local, usage::is_potentially_mutated};
@@ -251,9 +251,10 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
                             unwrappable.kind.error_variant_pattern()
                         };
 
-                        span_lint_and_then(
+                        span_lint_hir_and_then(
                             self.cx,
                             UNNECESSARY_UNWRAP,
+                            expr.hir_id,
                             expr.span,
                             &format!(
                                 "called `{}` on `{}` after checking its variant with `{}`",
@@ -283,9 +284,10 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
                             },
                         );
                     } else {
-                        span_lint_and_then(
+                        span_lint_hir_and_then(
                             self.cx,
                             PANICKING_UNWRAP,
+                            expr.hir_id,
                             expr.span,
                             &format!("this call to `{}()` will always panic",
                             method_name.ident.name),
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index a2772edf738..beb8bbe75d1 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -1539,9 +1539,13 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc
     None
 }
 
-/// Returns `true` if the lint is allowed in the current context
+/// Returns `true` if the lint is allowed in the current context. This is useful for
+/// skipping long running code when it's unnecessary
 ///
-/// Useful for skipping long running code when it's unnecessary
+/// This function should check the lint level for the same node, that the lint will
+/// be emitted at. If the information is buffered to be emitted at a later point, please
+/// make sure to use `span_lint_hir` functions to emit the lint. This ensures that
+/// expectations at the checked nodes will be fulfilled.
 pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
     cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
 }
diff --git a/tests/ui/escape_analysis.rs b/tests/ui/boxed_local.rs
index 13e2b6c7a2e..4639f00a8d8 100644
--- a/tests/ui/escape_analysis.rs
+++ b/tests/ui/boxed_local.rs
@@ -1,4 +1,5 @@
 #![feature(box_syntax)]
+#![feature(lint_reasons)]
 #![allow(
     clippy::borrowed_box,
     clippy::needless_pass_by_value,
@@ -202,3 +203,7 @@ mod issue4804 {
         fn foo(x: Box<u32>) {}
     }
 }
+
+fn check_expect(#[expect(clippy::boxed_local)] x: Box<A>) {
+    x.foo();
+}
diff --git a/tests/ui/escape_analysis.stderr b/tests/ui/boxed_local.stderr
index 4a82b4419f9..9036529f39c 100644
--- a/tests/ui/escape_analysis.stderr
+++ b/tests/ui/boxed_local.stderr
@@ -1,5 +1,5 @@
 error: local variable doesn't need to be boxed here
-  --> $DIR/escape_analysis.rs:40:13
+  --> $DIR/boxed_local.rs:41:13
    |
 LL | fn warn_arg(x: Box<A>) {
    |             ^
@@ -7,19 +7,19 @@ LL | fn warn_arg(x: Box<A>) {
    = note: `-D clippy::boxed-local` implied by `-D warnings`
 
 error: local variable doesn't need to be boxed here
-  --> $DIR/escape_analysis.rs:131:12
+  --> $DIR/boxed_local.rs:132:12
    |
 LL | pub fn new(_needs_name: Box<PeekableSeekable<&()>>) -> () {}
    |            ^^^^^^^^^^^
 
 error: local variable doesn't need to be boxed here
-  --> $DIR/escape_analysis.rs:195:44
+  --> $DIR/boxed_local.rs:196:44
    |
 LL |         fn default_impl_x(self: Box<Self>, x: Box<u32>) -> u32 {
    |                                            ^
 
 error: local variable doesn't need to be boxed here
-  --> $DIR/escape_analysis.rs:202:16
+  --> $DIR/boxed_local.rs:203:16
    |
 LL |         fn foo(x: Box<u32>) {}
    |                ^
diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs
index ee3fdfabe9d..82dce81979f 100644
--- a/tests/ui/checked_unwrap/simple_conditionals.rs
+++ b/tests/ui/checked_unwrap/simple_conditionals.rs
@@ -1,3 +1,4 @@
+#![feature(lint_reasons)]
 #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
 #![allow(clippy::if_same_then_else, clippy::branches_sharing_code)]
 
@@ -84,3 +85,18 @@ fn main() {
 
     assert!(x.is_ok(), "{:?}", x.unwrap_err()); // ok, it's a common test pattern
 }
+
+fn check_expect() {
+    let x = Some(());
+    if x.is_some() {
+        #[expect(clippy::unnecessary_unwrap)]
+        x.unwrap(); // unnecessary
+        #[expect(clippy::unnecessary_unwrap)]
+        x.expect("an error message"); // unnecessary
+    } else {
+        #[expect(clippy::panicking_unwrap)]
+        x.unwrap(); // will panic
+        #[expect(clippy::panicking_unwrap)]
+        x.expect("an error message"); // will panic
+    }
+}
diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr
index 34131592802..ef688274222 100644
--- a/tests/ui/checked_unwrap/simple_conditionals.stderr
+++ b/tests/ui/checked_unwrap/simple_conditionals.stderr
@@ -1,5 +1,5 @@
 error: called `unwrap` on `x` after checking its variant with `is_some`
-  --> $DIR/simple_conditionals.rs:39:9
+  --> $DIR/simple_conditionals.rs:40:9
    |
 LL |     if x.is_some() {
    |     -------------- help: try: `if let Some(..) = x`
@@ -7,13 +7,13 @@ LL |         x.unwrap(); // unnecessary
    |         ^^^^^^^^^^
    |
 note: the lint level is defined here
-  --> $DIR/simple_conditionals.rs:1:35
+  --> $DIR/simple_conditionals.rs:2:35
    |
 LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: called `expect` on `x` after checking its variant with `is_some`
-  --> $DIR/simple_conditionals.rs:40:9
+  --> $DIR/simple_conditionals.rs:41:9
    |
 LL |     if x.is_some() {
    |     -------------- help: try: `if let Some(..) = x`
@@ -22,7 +22,7 @@ LL |         x.expect("an error message"); // unnecessary
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this call to `unwrap()` will always panic
-  --> $DIR/simple_conditionals.rs:42:9
+  --> $DIR/simple_conditionals.rs:43:9
    |
 LL |     if x.is_some() {
    |        ----------- because of this check
@@ -31,13 +31,13 @@ LL |         x.unwrap(); // will panic
    |         ^^^^^^^^^^
    |
 note: the lint level is defined here
-  --> $DIR/simple_conditionals.rs:1:9
+  --> $DIR/simple_conditionals.rs:2:9
    |
 LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this call to `expect()` will always panic
-  --> $DIR/simple_conditionals.rs:43:9
+  --> $DIR/simple_conditionals.rs:44:9
    |
 LL |     if x.is_some() {
    |        ----------- because of this check
@@ -46,7 +46,7 @@ LL |         x.expect("an error message"); // will panic
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this call to `unwrap()` will always panic
-  --> $DIR/simple_conditionals.rs:46:9
+  --> $DIR/simple_conditionals.rs:47:9
    |
 LL |     if x.is_none() {
    |        ----------- because of this check
@@ -54,7 +54,7 @@ LL |         x.unwrap(); // will panic
    |         ^^^^^^^^^^
 
 error: called `unwrap` on `x` after checking its variant with `is_none`
-  --> $DIR/simple_conditionals.rs:48:9
+  --> $DIR/simple_conditionals.rs:49:9
    |
 LL |     if x.is_none() {
    |     -------------- help: try: `if let Some(..) = x`
@@ -63,7 +63,7 @@ LL |         x.unwrap(); // unnecessary
    |         ^^^^^^^^^^
 
 error: called `unwrap` on `x` after checking its variant with `is_some`
-  --> $DIR/simple_conditionals.rs:7:13
+  --> $DIR/simple_conditionals.rs:8:13
    |
 LL |         if $a.is_some() {
    |         --------------- help: try: `if let Some(..) = x`
@@ -76,7 +76,7 @@ LL |     m!(x);
    = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: called `unwrap` on `x` after checking its variant with `is_ok`
-  --> $DIR/simple_conditionals.rs:56:9
+  --> $DIR/simple_conditionals.rs:57:9
    |
 LL |     if x.is_ok() {
    |     ------------ help: try: `if let Ok(..) = x`
@@ -84,7 +84,7 @@ LL |         x.unwrap(); // unnecessary
    |         ^^^^^^^^^^
 
 error: called `expect` on `x` after checking its variant with `is_ok`
-  --> $DIR/simple_conditionals.rs:57:9
+  --> $DIR/simple_conditionals.rs:58:9
    |
 LL |     if x.is_ok() {
    |     ------------ help: try: `if let Ok(..) = x`
@@ -93,7 +93,7 @@ LL |         x.expect("an error message"); // unnecessary
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this call to `unwrap_err()` will always panic
-  --> $DIR/simple_conditionals.rs:58:9
+  --> $DIR/simple_conditionals.rs:59:9
    |
 LL |     if x.is_ok() {
    |        --------- because of this check
@@ -102,7 +102,7 @@ LL |         x.unwrap_err(); // will panic
    |         ^^^^^^^^^^^^^^
 
 error: this call to `unwrap()` will always panic
-  --> $DIR/simple_conditionals.rs:60:9
+  --> $DIR/simple_conditionals.rs:61:9
    |
 LL |     if x.is_ok() {
    |        --------- because of this check
@@ -111,7 +111,7 @@ LL |         x.unwrap(); // will panic
    |         ^^^^^^^^^^
 
 error: this call to `expect()` will always panic
-  --> $DIR/simple_conditionals.rs:61:9
+  --> $DIR/simple_conditionals.rs:62:9
    |
 LL |     if x.is_ok() {
    |        --------- because of this check
@@ -120,7 +120,7 @@ LL |         x.expect("an error message"); // will panic
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: called `unwrap_err` on `x` after checking its variant with `is_ok`
-  --> $DIR/simple_conditionals.rs:62:9
+  --> $DIR/simple_conditionals.rs:63:9
    |
 LL |     if x.is_ok() {
    |     ------------ help: try: `if let Err(..) = x`
@@ -129,7 +129,7 @@ LL |         x.unwrap_err(); // unnecessary
    |         ^^^^^^^^^^^^^^
 
 error: this call to `unwrap()` will always panic
-  --> $DIR/simple_conditionals.rs:65:9
+  --> $DIR/simple_conditionals.rs:66:9
    |
 LL |     if x.is_err() {
    |        ---------- because of this check
@@ -137,7 +137,7 @@ LL |         x.unwrap(); // will panic
    |         ^^^^^^^^^^
 
 error: called `unwrap_err` on `x` after checking its variant with `is_err`
-  --> $DIR/simple_conditionals.rs:66:9
+  --> $DIR/simple_conditionals.rs:67:9
    |
 LL |     if x.is_err() {
    |     ------------- help: try: `if let Err(..) = x`
@@ -146,7 +146,7 @@ LL |         x.unwrap_err(); // unnecessary
    |         ^^^^^^^^^^^^^^
 
 error: called `unwrap` on `x` after checking its variant with `is_err`
-  --> $DIR/simple_conditionals.rs:68:9
+  --> $DIR/simple_conditionals.rs:69:9
    |
 LL |     if x.is_err() {
    |     ------------- help: try: `if let Ok(..) = x`
@@ -155,7 +155,7 @@ LL |         x.unwrap(); // unnecessary
    |         ^^^^^^^^^^
 
 error: this call to `unwrap_err()` will always panic
-  --> $DIR/simple_conditionals.rs:69:9
+  --> $DIR/simple_conditionals.rs:70:9
    |
 LL |     if x.is_err() {
    |        ---------- because of this check
diff --git a/tests/ui/implicit_return.fixed b/tests/ui/implicit_return.fixed
index a51f7bc6a29..5e55b8b6739 100644
--- a/tests/ui/implicit_return.fixed
+++ b/tests/ui/implicit_return.fixed
@@ -1,5 +1,5 @@
 // run-rustfix
-
+#![feature(lint_reasons)]
 #![warn(clippy::implicit_return)]
 #![allow(clippy::needless_return, clippy::needless_bool, unused, clippy::never_loop)]
 
@@ -128,3 +128,13 @@ async fn foo() -> bool {
 }
 
 fn main() {}
+
+fn check_expect() -> bool {
+    if true {
+        // no error!
+        return true;
+    }
+
+    #[expect(clippy::implicit_return)]
+    true
+}
diff --git a/tests/ui/implicit_return.rs b/tests/ui/implicit_return.rs
index 03f8ec49d51..76f0a980352 100644
--- a/tests/ui/implicit_return.rs
+++ b/tests/ui/implicit_return.rs
@@ -1,5 +1,5 @@
 // run-rustfix
-
+#![feature(lint_reasons)]
 #![warn(clippy::implicit_return)]
 #![allow(clippy::needless_return, clippy::needless_bool, unused, clippy::never_loop)]
 
@@ -128,3 +128,13 @@ async fn foo() -> bool {
 }
 
 fn main() {}
+
+fn check_expect() -> bool {
+    if true {
+        // no error!
+        return true;
+    }
+
+    #[expect(clippy::implicit_return)]
+    true
+}
diff --git a/tests/ui/logic_bug.rs b/tests/ui/logic_bug.rs
index 4eaa2dd98eb..dd6b1db5f70 100644
--- a/tests/ui/logic_bug.rs
+++ b/tests/ui/logic_bug.rs
@@ -1,3 +1,4 @@
+#![feature(lint_reasons)]
 #![allow(unused, clippy::diverging_sub_expression)]
 #![warn(clippy::logic_bug)]
 
@@ -24,3 +25,10 @@ fn equality_stuff() {
     let _ = a > b && a <= b;
     let _ = a > b && a == b;
 }
+
+fn check_expect() {
+    let a: i32 = unimplemented!();
+    let b: i32 = unimplemented!();
+    #[expect(clippy::logic_bug)]
+    let _ = a < b && a >= b;
+}
diff --git a/tests/ui/logic_bug.stderr b/tests/ui/logic_bug.stderr
index 8f55e1c8ad8..4021fbf4570 100644
--- a/tests/ui/logic_bug.stderr
+++ b/tests/ui/logic_bug.stderr
@@ -1,60 +1,60 @@
 error: this boolean expression contains a logic bug
-  --> $DIR/logic_bug.rs:10:13
+  --> $DIR/logic_bug.rs:11:13
    |
 LL |     let _ = a && b || a;
    |             ^^^^^^^^^^^ help: it would look like the following: `a`
    |
    = note: `-D clippy::logic-bug` implied by `-D warnings`
 help: this expression can be optimized out by applying boolean operations to the outer expression
-  --> $DIR/logic_bug.rs:10:18
+  --> $DIR/logic_bug.rs:11:18
    |
 LL |     let _ = a && b || a;
    |                  ^
 
 error: this boolean expression contains a logic bug
-  --> $DIR/logic_bug.rs:12:13
+  --> $DIR/logic_bug.rs:13:13
    |
 LL |     let _ = false && a;
    |             ^^^^^^^^^^ help: it would look like the following: `false`
    |
 help: this expression can be optimized out by applying boolean operations to the outer expression
-  --> $DIR/logic_bug.rs:12:22
+  --> $DIR/logic_bug.rs:13:22
    |
 LL |     let _ = false && a;
    |                      ^
 
 error: this boolean expression contains a logic bug
-  --> $DIR/logic_bug.rs:22:13
+  --> $DIR/logic_bug.rs:23:13
    |
 LL |     let _ = a == b && a != b;
    |             ^^^^^^^^^^^^^^^^ help: it would look like the following: `false`
    |
 help: this expression can be optimized out by applying boolean operations to the outer expression
-  --> $DIR/logic_bug.rs:22:13
+  --> $DIR/logic_bug.rs:23:13
    |
 LL |     let _ = a == b && a != b;
    |             ^^^^^^
 
 error: this boolean expression contains a logic bug
-  --> $DIR/logic_bug.rs:23:13
+  --> $DIR/logic_bug.rs:24:13
    |
 LL |     let _ = a < b && a >= b;
    |             ^^^^^^^^^^^^^^^ help: it would look like the following: `false`
    |
 help: this expression can be optimized out by applying boolean operations to the outer expression
-  --> $DIR/logic_bug.rs:23:13
+  --> $DIR/logic_bug.rs:24:13
    |
 LL |     let _ = a < b && a >= b;
    |             ^^^^^
 
 error: this boolean expression contains a logic bug
-  --> $DIR/logic_bug.rs:24:13
+  --> $DIR/logic_bug.rs:25:13
    |
 LL |     let _ = a > b && a <= b;
    |             ^^^^^^^^^^^^^^^ help: it would look like the following: `false`
    |
 help: this expression can be optimized out by applying boolean operations to the outer expression
-  --> $DIR/logic_bug.rs:24:13
+  --> $DIR/logic_bug.rs:25:13
    |
 LL |     let _ = a > b && a <= b;
    |             ^^^^^
diff --git a/tests/ui/macro_use_imports.fixed b/tests/ui/macro_use_imports.fixed
index a83c8ba0b64..e612480d264 100644
--- a/tests/ui/macro_use_imports.fixed
+++ b/tests/ui/macro_use_imports.fixed
@@ -4,6 +4,7 @@
 // run-rustfix
 // ignore-32bit
 
+#![feature(lint_reasons)]
 #![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)]
 #![allow(clippy::single_component_path_imports)]
 #![warn(clippy::macro_use_imports)]
diff --git a/tests/ui/macro_use_imports.rs b/tests/ui/macro_use_imports.rs
index e26a7545ea6..b34817cc3b2 100644
--- a/tests/ui/macro_use_imports.rs
+++ b/tests/ui/macro_use_imports.rs
@@ -4,6 +4,7 @@
 // run-rustfix
 // ignore-32bit
 
+#![feature(lint_reasons)]
 #![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)]
 #![allow(clippy::single_component_path_imports)]
 #![warn(clippy::macro_use_imports)]
diff --git a/tests/ui/macro_use_imports.stderr b/tests/ui/macro_use_imports.stderr
index 9028a636e7f..bf7b6edd0e3 100644
--- a/tests/ui/macro_use_imports.stderr
+++ b/tests/ui/macro_use_imports.stderr
@@ -1,28 +1,28 @@
 error: `macro_use` attributes are no longer needed in the Rust 2018 edition
-  --> $DIR/macro_use_imports.rs:18:5
+  --> $DIR/macro_use_imports.rs:23:5
    |
 LL |     #[macro_use]
-   |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, function_macro, ty_macro, inner_mod_macro, pub_in_private_macro};`
+   |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};`
    |
    = note: `-D clippy::macro-use-imports` implied by `-D warnings`
 
 error: `macro_use` attributes are no longer needed in the Rust 2018 edition
-  --> $DIR/macro_use_imports.rs:20:5
+  --> $DIR/macro_use_imports.rs:21:5
    |
 LL |     #[macro_use]
    |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;`
 
 error: `macro_use` attributes are no longer needed in the Rust 2018 edition
-  --> $DIR/macro_use_imports.rs:22:5
+  --> $DIR/macro_use_imports.rs:25:5
    |
 LL |     #[macro_use]
-   |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};`
+   |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;`
 
 error: `macro_use` attributes are no longer needed in the Rust 2018 edition
-  --> $DIR/macro_use_imports.rs:24:5
+  --> $DIR/macro_use_imports.rs:19:5
    |
 LL |     #[macro_use]
-   |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;`
+   |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, function_macro, ty_macro, inner_mod_macro, pub_in_private_macro};`
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/macro_use_imports_expect.rs b/tests/ui/macro_use_imports_expect.rs
new file mode 100644
index 00000000000..8a1b05da9ef
--- /dev/null
+++ b/tests/ui/macro_use_imports_expect.rs
@@ -0,0 +1,51 @@
+// aux-build:macro_rules.rs
+// aux-build:macro_use_helper.rs
+// aux-build:proc_macro_derive.rs
+// ignore-32bit
+
+#![feature(lint_reasons)]
+#![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)]
+#![allow(clippy::single_component_path_imports)]
+#![warn(clippy::macro_use_imports)]
+
+#[macro_use]
+extern crate macro_use_helper as mac;
+
+#[macro_use]
+extern crate proc_macro_derive as mini_mac;
+
+mod a {
+    #[expect(clippy::macro_use_imports)]
+    #[macro_use]
+    use mac;
+    #[expect(clippy::macro_use_imports)]
+    #[macro_use]
+    use mini_mac;
+    #[expect(clippy::macro_use_imports)]
+    #[macro_use]
+    use mac::inner;
+    #[expect(clippy::macro_use_imports)]
+    #[macro_use]
+    use mac::inner::nested;
+
+    #[derive(ClippyMiniMacroTest)]
+    struct Test;
+
+    fn test() {
+        pub_macro!();
+        inner_mod_macro!();
+        pub_in_private_macro!(_var);
+        function_macro!();
+        let v: ty_macro!() = Vec::default();
+
+        inner::try_err!();
+        inner::foofoo!();
+        nested::string_add!();
+    }
+}
+
+// issue #7015, ICE due to calling `module_children` with local `DefId`
+#[macro_use]
+use a as b;
+
+fn main() {}
diff --git a/tests/ui/manual_non_exhaustive_enum.rs b/tests/ui/manual_non_exhaustive_enum.rs
index f23c6d69b4c..03b2433f666 100644
--- a/tests/ui/manual_non_exhaustive_enum.rs
+++ b/tests/ui/manual_non_exhaustive_enum.rs
@@ -1,3 +1,4 @@
+#![feature(lint_reasons)]
 #![warn(clippy::manual_non_exhaustive)]
 #![allow(unused)]
 
@@ -75,4 +76,12 @@ fn foo(x: &mut UsedHidden) {
     }
 }
 
+#[expect(clippy::manual_non_exhaustive)]
+enum ExpectLint {
+    A,
+    B,
+    #[doc(hidden)]
+    _C,
+}
+
 fn main() {}
diff --git a/tests/ui/manual_non_exhaustive_enum.stderr b/tests/ui/manual_non_exhaustive_enum.stderr
index 317a45d2cbd..144fe86df55 100644
--- a/tests/ui/manual_non_exhaustive_enum.stderr
+++ b/tests/ui/manual_non_exhaustive_enum.stderr
@@ -1,5 +1,5 @@
 error: this seems like a manual implementation of the non-exhaustive pattern
-  --> $DIR/manual_non_exhaustive_enum.rs:4:1
+  --> $DIR/manual_non_exhaustive_enum.rs:5:1
    |
 LL |   enum E {
    |   ^-----
@@ -15,13 +15,13 @@ LL | | }
    |
    = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
 help: remove this variant
-  --> $DIR/manual_non_exhaustive_enum.rs:8:5
+  --> $DIR/manual_non_exhaustive_enum.rs:9:5
    |
 LL |     _C,
    |     ^^
 
 error: this seems like a manual implementation of the non-exhaustive pattern
-  --> $DIR/manual_non_exhaustive_enum.rs:13:1
+  --> $DIR/manual_non_exhaustive_enum.rs:14:1
    |
 LL | / enum Ep {
 LL | |     A,
@@ -32,7 +32,7 @@ LL | | }
    | |_^
    |
 help: remove this variant
-  --> $DIR/manual_non_exhaustive_enum.rs:17:5
+  --> $DIR/manual_non_exhaustive_enum.rs:18:5
    |
 LL |     _C,
    |     ^^
diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed
index 166d9bea2e1..0bc0d0011ef 100644
--- a/tests/ui/needless_return.fixed
+++ b/tests/ui/needless_return.fixed
@@ -1,5 +1,6 @@
 // run-rustfix
 
+#![feature(lint_reasons)]
 #![feature(let_else)]
 #![allow(unused)]
 #![allow(
@@ -227,4 +228,13 @@ fn needless_return_macro() -> String {
     format!("Hello {}", "world!")
 }
 
+fn check_expect() -> bool {
+    if true {
+        // no error!
+        return true;
+    }
+    #[expect(clippy::needless_return)]
+    return true;
+}
+
 fn main() {}
diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs
index f38d3c3fcd9..eb9f72e8e78 100644
--- a/tests/ui/needless_return.rs
+++ b/tests/ui/needless_return.rs
@@ -1,5 +1,6 @@
 // run-rustfix
 
+#![feature(lint_reasons)]
 #![feature(let_else)]
 #![allow(unused)]
 #![allow(
@@ -227,4 +228,13 @@ fn needless_return_macro() -> String {
     return format!("Hello {}", "world!");
 }
 
+fn check_expect() -> bool {
+    if true {
+        // no error!
+        return true;
+    }
+    #[expect(clippy::needless_return)]
+    return true;
+}
+
 fn main() {}
diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr
index cadee6e00df..83ff0763869 100644
--- a/tests/ui/needless_return.stderr
+++ b/tests/ui/needless_return.stderr
@@ -1,5 +1,5 @@
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:26:5
+  --> $DIR/needless_return.rs:27:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^^ help: remove `return`: `true`
@@ -7,217 +7,217 @@ LL |     return true;
    = note: `-D clippy::needless-return` implied by `-D warnings`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:30:5
+  --> $DIR/needless_return.rs:31:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:35:9
+  --> $DIR/needless_return.rs:36:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:37:9
+  --> $DIR/needless_return.rs:38:9
    |
 LL |         return false;
    |         ^^^^^^^^^^^^^ help: remove `return`: `false`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:43:17
+  --> $DIR/needless_return.rs:44:17
    |
 LL |         true => return false,
    |                 ^^^^^^^^^^^^ help: remove `return`: `false`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:45:13
+  --> $DIR/needless_return.rs:46:13
    |
 LL |             return true;
    |             ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:52:9
+  --> $DIR/needless_return.rs:53:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:54:16
+  --> $DIR/needless_return.rs:55:16
    |
 LL |     let _ = || return true;
    |                ^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:58:5
+  --> $DIR/needless_return.rs:59:5
    |
 LL |     return the_answer!();
    |     ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `the_answer!()`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:62:5
+  --> $DIR/needless_return.rs:63:5
    |
 LL |     return;
    |     ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:67:9
+  --> $DIR/needless_return.rs:68:9
    |
 LL |         return;
    |         ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:69:9
+  --> $DIR/needless_return.rs:70:9
    |
 LL |         return;
    |         ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:76:14
+  --> $DIR/needless_return.rs:77:14
    |
 LL |         _ => return,
    |              ^^^^^^ help: replace `return` with a unit value: `()`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:85:13
+  --> $DIR/needless_return.rs:86:13
    |
 LL |             return;
    |             ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:87:14
+  --> $DIR/needless_return.rs:88:14
    |
 LL |         _ => return,
    |              ^^^^^^ help: replace `return` with a unit value: `()`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:100:9
+  --> $DIR/needless_return.rs:101:9
    |
 LL |         return String::from("test");
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:102:9
+  --> $DIR/needless_return.rs:103:9
    |
 LL |         return String::new();
    |         ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:124:32
+  --> $DIR/needless_return.rs:125:32
    |
 LL |         bar.unwrap_or_else(|_| return)
    |                                ^^^^^^ help: replace `return` with an empty block: `{}`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:129:13
+  --> $DIR/needless_return.rs:130:13
    |
 LL |             return;
    |             ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:131:20
+  --> $DIR/needless_return.rs:132:20
    |
 LL |         let _ = || return;
    |                    ^^^^^^ help: replace `return` with an empty block: `{}`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:137:32
+  --> $DIR/needless_return.rs:138:32
    |
 LL |         res.unwrap_or_else(|_| return Foo)
    |                                ^^^^^^^^^^ help: remove `return`: `Foo`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:146:5
+  --> $DIR/needless_return.rs:147:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:150:5
+  --> $DIR/needless_return.rs:151:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:155:9
+  --> $DIR/needless_return.rs:156:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:157:9
+  --> $DIR/needless_return.rs:158:9
    |
 LL |         return false;
    |         ^^^^^^^^^^^^^ help: remove `return`: `false`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:163:17
+  --> $DIR/needless_return.rs:164:17
    |
 LL |         true => return false,
    |                 ^^^^^^^^^^^^ help: remove `return`: `false`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:165:13
+  --> $DIR/needless_return.rs:166:13
    |
 LL |             return true;
    |             ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:172:9
+  --> $DIR/needless_return.rs:173:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:174:16
+  --> $DIR/needless_return.rs:175:16
    |
 LL |     let _ = || return true;
    |                ^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:178:5
+  --> $DIR/needless_return.rs:179:5
    |
 LL |     return the_answer!();
    |     ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `the_answer!()`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:182:5
+  --> $DIR/needless_return.rs:183:5
    |
 LL |     return;
    |     ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:187:9
+  --> $DIR/needless_return.rs:188:9
    |
 LL |         return;
    |         ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:189:9
+  --> $DIR/needless_return.rs:190:9
    |
 LL |         return;
    |         ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:196:14
+  --> $DIR/needless_return.rs:197:14
    |
 LL |         _ => return,
    |              ^^^^^^ help: replace `return` with a unit value: `()`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:209:9
+  --> $DIR/needless_return.rs:210:9
    |
 LL |         return String::from("test");
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:211:9
+  --> $DIR/needless_return.rs:212:9
    |
 LL |         return String::new();
    |         ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:227:5
+  --> $DIR/needless_return.rs:228:5
    |
 LL |     return format!("Hello {}", "world!");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `format!("Hello {}", "world!")`
diff --git a/tests/ui/nonminimal_bool.rs b/tests/ui/nonminimal_bool.rs
index fa5743c1155..24ae62bb058 100644
--- a/tests/ui/nonminimal_bool.rs
+++ b/tests/ui/nonminimal_bool.rs
@@ -1,3 +1,4 @@
+#![feature(lint_reasons)]
 #![allow(unused, clippy::diverging_sub_expression)]
 #![warn(clippy::nonminimal_bool)]
 
@@ -50,3 +51,9 @@ fn issue4548() {
 
     if i != j && f(i, j) != 0 || i == j && f(i, j) != 1 {}
 }
+
+fn check_expect() {
+    let a: bool = unimplemented!();
+    #[expect(clippy::nonminimal_bool)]
+    let _ = !!a;
+}
diff --git a/tests/ui/nonminimal_bool.stderr b/tests/ui/nonminimal_bool.stderr
index bb93cbbd5e1..fc6a5ce1dc2 100644
--- a/tests/ui/nonminimal_bool.stderr
+++ b/tests/ui/nonminimal_bool.stderr
@@ -1,5 +1,5 @@
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:10:13
+  --> $DIR/nonminimal_bool.rs:11:13
    |
 LL |     let _ = !true;
    |             ^^^^^ help: try: `false`
@@ -7,43 +7,43 @@ LL |     let _ = !true;
    = note: `-D clippy::nonminimal-bool` implied by `-D warnings`
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:11:13
+  --> $DIR/nonminimal_bool.rs:12:13
    |
 LL |     let _ = !false;
    |             ^^^^^^ help: try: `true`
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:12:13
+  --> $DIR/nonminimal_bool.rs:13:13
    |
 LL |     let _ = !!a;
    |             ^^^ help: try: `a`
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:13:13
+  --> $DIR/nonminimal_bool.rs:14:13
    |
 LL |     let _ = false || a;
    |             ^^^^^^^^^^ help: try: `a`
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:17:13
+  --> $DIR/nonminimal_bool.rs:18:13
    |
 LL |     let _ = !(!a && b);
    |             ^^^^^^^^^^ help: try: `a || !b`
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:18:13
+  --> $DIR/nonminimal_bool.rs:19:13
    |
 LL |     let _ = !(!a || b);
    |             ^^^^^^^^^^ help: try: `a && !b`
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:19:13
+  --> $DIR/nonminimal_bool.rs:20:13
    |
 LL |     let _ = !a && !(b && c);
    |             ^^^^^^^^^^^^^^^ help: try: `!(a || b && c)`
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:27:13
+  --> $DIR/nonminimal_bool.rs:28:13
    |
 LL |     let _ = a == b && c == 5 && a == b;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -56,7 +56,7 @@ LL |     let _ = a == b && c == 5;
    |             ~~~~~~~~~~~~~~~~
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:28:13
+  --> $DIR/nonminimal_bool.rs:29:13
    |
 LL |     let _ = a == b || c == 5 || a == b;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -69,7 +69,7 @@ LL |     let _ = a == b || c == 5;
    |             ~~~~~~~~~~~~~~~~
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:29:13
+  --> $DIR/nonminimal_bool.rs:30:13
    |
 LL |     let _ = a == b && c == 5 && b == a;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -82,7 +82,7 @@ LL |     let _ = a == b && c == 5;
    |             ~~~~~~~~~~~~~~~~
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:30:13
+  --> $DIR/nonminimal_bool.rs:31:13
    |
 LL |     let _ = a != b || !(a != b || c == d);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -95,7 +95,7 @@ LL |     let _ = a != b || c != d;
    |             ~~~~~~~~~~~~~~~~
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:31:13
+  --> $DIR/nonminimal_bool.rs:32:13
    |
 LL |     let _ = a != b && !(a != b && c == d);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs
index 814bbc7af71..fd15001e540 100644
--- a/tests/ui/ptr_arg.rs
+++ b/tests/ui/ptr_arg.rs
@@ -1,3 +1,4 @@
+#![feature(lint_reasons)]
 #![allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
 #![warn(clippy::ptr_arg)]
 
@@ -109,9 +110,12 @@ mod issue_5644 {
         #[allow(clippy::ptr_arg)] _s: &String,
         #[allow(clippy::ptr_arg)] _p: &PathBuf,
         #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
+        #[expect(clippy::ptr_arg)] _expect: &Cow<[i32]>,
     ) {
     }
 
+    fn some_allowed(#[allow(clippy::ptr_arg)] _v: &Vec<u32>, _s: &String) {}
+
     struct S;
     impl S {
         fn allowed(
@@ -119,6 +123,7 @@ mod issue_5644 {
             #[allow(clippy::ptr_arg)] _s: &String,
             #[allow(clippy::ptr_arg)] _p: &PathBuf,
             #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
+            #[expect(clippy::ptr_arg)] _expect: &Cow<[i32]>,
         ) {
         }
     }
@@ -129,6 +134,7 @@ mod issue_5644 {
             #[allow(clippy::ptr_arg)] _s: &String,
             #[allow(clippy::ptr_arg)] _p: &PathBuf,
             #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
+            #[expect(clippy::ptr_arg)] _expect: &Cow<[i32]>,
         ) {
         }
     }
diff --git a/tests/ui/ptr_arg.stderr b/tests/ui/ptr_arg.stderr
index a9613daadde..bbf731c6d01 100644
--- a/tests/ui/ptr_arg.stderr
+++ b/tests/ui/ptr_arg.stderr
@@ -1,5 +1,5 @@
 error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:7:14
+  --> $DIR/ptr_arg.rs:8:14
    |
 LL | fn do_vec(x: &Vec<i64>) {
    |              ^^^^^^^^^ help: change this to: `&[i64]`
@@ -7,43 +7,43 @@ LL | fn do_vec(x: &Vec<i64>) {
    = note: `-D clippy::ptr-arg` implied by `-D warnings`
 
 error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:11:18
+  --> $DIR/ptr_arg.rs:12:18
    |
 LL | fn do_vec_mut(x: &mut Vec<i64>) {
    |                  ^^^^^^^^^^^^^ help: change this to: `&mut [i64]`
 
 error: writing `&String` instead of `&str` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:15:14
+  --> $DIR/ptr_arg.rs:16:14
    |
 LL | fn do_str(x: &String) {
    |              ^^^^^^^ help: change this to: `&str`
 
 error: writing `&mut String` instead of `&mut str` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:19:18
+  --> $DIR/ptr_arg.rs:20:18
    |
 LL | fn do_str_mut(x: &mut String) {
    |                  ^^^^^^^^^^^ help: change this to: `&mut str`
 
 error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:23:15
+  --> $DIR/ptr_arg.rs:24:15
    |
 LL | fn do_path(x: &PathBuf) {
    |               ^^^^^^^^ help: change this to: `&Path`
 
 error: writing `&mut PathBuf` instead of `&mut Path` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:27:19
+  --> $DIR/ptr_arg.rs:28:19
    |
 LL | fn do_path_mut(x: &mut PathBuf) {
    |                   ^^^^^^^^^^^^ help: change this to: `&mut Path`
 
 error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:35:18
+  --> $DIR/ptr_arg.rs:36:18
    |
 LL |     fn do_vec(x: &Vec<i64>);
    |                  ^^^^^^^^^ help: change this to: `&[i64]`
 
 error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:48:14
+  --> $DIR/ptr_arg.rs:49:14
    |
 LL | fn cloned(x: &Vec<u8>) -> Vec<u8> {
    |              ^^^^^^^^
@@ -59,7 +59,7 @@ LL |     let i = (e).clone();
  ...
 
 error: writing `&String` instead of `&str` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:57:18
+  --> $DIR/ptr_arg.rs:58:18
    |
 LL | fn str_cloned(x: &String) -> String {
    |                  ^^^^^^^
@@ -75,7 +75,7 @@ LL ~     x.to_owned()
    |
 
 error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:65:19
+  --> $DIR/ptr_arg.rs:66:19
    |
 LL | fn path_cloned(x: &PathBuf) -> PathBuf {
    |                   ^^^^^^^^
@@ -91,7 +91,7 @@ LL ~     x.to_path_buf()
    |
 
 error: writing `&String` instead of `&str` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:73:44
+  --> $DIR/ptr_arg.rs:74:44
    |
 LL | fn false_positive_capacity(x: &Vec<u8>, y: &String) {
    |                                            ^^^^^^^
@@ -105,13 +105,19 @@ LL ~     let c = y;
    |
 
 error: using a reference to `Cow` is not recommended
-  --> $DIR/ptr_arg.rs:87:25
+  --> $DIR/ptr_arg.rs:88:25
    |
 LL | fn test_cow_with_ref(c: &Cow<[i32]>) {}
    |                         ^^^^^^^^^^^ help: change this to: `&[i32]`
 
+error: writing `&String` instead of `&str` involves a new object where a slice will do
+  --> $DIR/ptr_arg.rs:117:66
+   |
+LL |     fn some_allowed(#[allow(clippy::ptr_arg)] _v: &Vec<u32>, _s: &String) {}
+   |                                                                  ^^^^^^^ help: change this to: `&str`
+
 error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:140:21
+  --> $DIR/ptr_arg.rs:146:21
    |
 LL |     fn foo_vec(vec: &Vec<u8>) {
    |                     ^^^^^^^^
@@ -124,7 +130,7 @@ LL ~         let _ = vec.to_owned().clone();
    |
 
 error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:145:23
+  --> $DIR/ptr_arg.rs:151:23
    |
 LL |     fn foo_path(path: &PathBuf) {
    |                       ^^^^^^^^
@@ -137,7 +143,7 @@ LL ~         let _ = path.to_path_buf().clone();
    |
 
 error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:150:21
+  --> $DIR/ptr_arg.rs:156:21
    |
 LL |     fn foo_str(str: &PathBuf) {
    |                     ^^^^^^^^
@@ -150,10 +156,10 @@ LL ~         let _ = str.to_path_buf().clone();
    |
 
 error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:156:29
+  --> $DIR/ptr_arg.rs:162:29
    |
 LL | fn mut_vec_slice_methods(v: &mut Vec<u32>) {
    |                             ^^^^^^^^^^^^^ help: change this to: `&mut [u32]`
 
-error: aborting due to 16 previous errors
+error: aborting due to 17 previous errors
 
diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed
index 1525f6a93df..da52c0acf93 100644
--- a/tests/ui/redundant_clone.fixed
+++ b/tests/ui/redundant_clone.fixed
@@ -1,6 +1,7 @@
 // run-rustfix
 // rustfix-only-machine-applicable
 
+#![feature(lint_reasons)]
 #![allow(clippy::implicit_clone, clippy::drop_non_drop)]
 use std::ffi::OsString;
 use std::path::Path;
@@ -29,6 +30,10 @@ fn main() {
     #[allow(clippy::redundant_clone)]
     let _s = String::new().to_string();
 
+    // Check that lint level works
+    #[expect(clippy::redundant_clone)]
+    let _s = String::new().to_string();
+
     let tup = (String::from("foo"),);
     let _t = tup.0;
 
diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs
index 2f82aefd928..5867d019dbb 100644
--- a/tests/ui/redundant_clone.rs
+++ b/tests/ui/redundant_clone.rs
@@ -1,6 +1,7 @@
 // run-rustfix
 // rustfix-only-machine-applicable
 
+#![feature(lint_reasons)]
 #![allow(clippy::implicit_clone, clippy::drop_non_drop)]
 use std::ffi::OsString;
 use std::path::Path;
@@ -29,6 +30,10 @@ fn main() {
     #[allow(clippy::redundant_clone)]
     let _s = String::new().to_string();
 
+    // Check that lint level works
+    #[expect(clippy::redundant_clone)]
+    let _s = String::new().to_string();
+
     let tup = (String::from("foo"),);
     let _t = tup.0.clone();
 
diff --git a/tests/ui/redundant_clone.stderr b/tests/ui/redundant_clone.stderr
index 9f59017b261..aa1dd7cbb45 100644
--- a/tests/ui/redundant_clone.stderr
+++ b/tests/ui/redundant_clone.stderr
@@ -1,180 +1,180 @@
 error: redundant clone
-  --> $DIR/redundant_clone.rs:9:42
+  --> $DIR/redundant_clone.rs:10:42
    |
 LL |     let _s = ["lorem", "ipsum"].join(" ").to_string();
    |                                          ^^^^^^^^^^^^ help: remove this
    |
    = note: `-D clippy::redundant-clone` implied by `-D warnings`
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:9:14
+  --> $DIR/redundant_clone.rs:10:14
    |
 LL |     let _s = ["lorem", "ipsum"].join(" ").to_string();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:12:15
+  --> $DIR/redundant_clone.rs:13:15
    |
 LL |     let _s = s.clone();
    |               ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:12:14
+  --> $DIR/redundant_clone.rs:13:14
    |
 LL |     let _s = s.clone();
    |              ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:15:15
+  --> $DIR/redundant_clone.rs:16:15
    |
 LL |     let _s = s.to_string();
    |               ^^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:15:14
+  --> $DIR/redundant_clone.rs:16:14
    |
 LL |     let _s = s.to_string();
    |              ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:18:15
+  --> $DIR/redundant_clone.rs:19:15
    |
 LL |     let _s = s.to_owned();
    |               ^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:18:14
+  --> $DIR/redundant_clone.rs:19:14
    |
 LL |     let _s = s.to_owned();
    |              ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:20:42
+  --> $DIR/redundant_clone.rs:21:42
    |
 LL |     let _s = Path::new("/a/b/").join("c").to_owned();
    |                                          ^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:20:14
+  --> $DIR/redundant_clone.rs:21:14
    |
 LL |     let _s = Path::new("/a/b/").join("c").to_owned();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:22:42
+  --> $DIR/redundant_clone.rs:23:42
    |
 LL |     let _s = Path::new("/a/b/").join("c").to_path_buf();
    |                                          ^^^^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:22:14
+  --> $DIR/redundant_clone.rs:23:14
    |
 LL |     let _s = Path::new("/a/b/").join("c").to_path_buf();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:24:29
+  --> $DIR/redundant_clone.rs:25:29
    |
 LL |     let _s = OsString::new().to_owned();
    |                             ^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:24:14
+  --> $DIR/redundant_clone.rs:25:14
    |
 LL |     let _s = OsString::new().to_owned();
    |              ^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:26:29
+  --> $DIR/redundant_clone.rs:27:29
    |
 LL |     let _s = OsString::new().to_os_string();
    |                             ^^^^^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:26:14
+  --> $DIR/redundant_clone.rs:27:14
    |
 LL |     let _s = OsString::new().to_os_string();
    |              ^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:33:19
+  --> $DIR/redundant_clone.rs:38:19
    |
 LL |     let _t = tup.0.clone();
    |                   ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:33:14
+  --> $DIR/redundant_clone.rs:38:14
    |
 LL |     let _t = tup.0.clone();
    |              ^^^^^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:65:25
+  --> $DIR/redundant_clone.rs:70:25
    |
 LL |     if b { (a.clone(), a.clone()) } else { (Alpha, a) }
    |                         ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:65:24
+  --> $DIR/redundant_clone.rs:70:24
    |
 LL |     if b { (a.clone(), a.clone()) } else { (Alpha, a) }
    |                        ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:122:15
+  --> $DIR/redundant_clone.rs:127:15
    |
 LL |     let _s = s.clone();
    |               ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:122:14
+  --> $DIR/redundant_clone.rs:127:14
    |
 LL |     let _s = s.clone();
    |              ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:123:15
+  --> $DIR/redundant_clone.rs:128:15
    |
 LL |     let _t = t.clone();
    |               ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:123:14
+  --> $DIR/redundant_clone.rs:128:14
    |
 LL |     let _t = t.clone();
    |              ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:133:19
+  --> $DIR/redundant_clone.rs:138:19
    |
 LL |         let _f = f.clone();
    |                   ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:133:18
+  --> $DIR/redundant_clone.rs:138:18
    |
 LL |         let _f = f.clone();
    |                  ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:145:14
+  --> $DIR/redundant_clone.rs:150:14
    |
 LL |     let y = x.clone().join("matthias");
    |              ^^^^^^^^ help: remove this
    |
 note: cloned value is neither consumed nor mutated
-  --> $DIR/redundant_clone.rs:145:13
+  --> $DIR/redundant_clone.rs:150:13
    |
 LL |     let y = x.clone().join("matthias");
    |             ^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:199:11
+  --> $DIR/redundant_clone.rs:204:11
    |
 LL |     foo(&x.clone(), move || {
    |           ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:199:10
+  --> $DIR/redundant_clone.rs:204:10
    |
 LL |     foo(&x.clone(), move || {
    |          ^