about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/copies.rs25
-rw-r--r--clippy_lints/src/matches/match_same_arms.rs122
-rw-r--r--clippy_utils/src/lib.rs43
-rw-r--r--tests/ui-toml/ifs_same_cond/ifs_same_cond.rs2
-rw-r--r--tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr12
-rw-r--r--tests/ui/ifs_same_cond.rs12
-rw-r--r--tests/ui/ifs_same_cond.stderr62
-rw-r--r--tests/ui/match_same_arms.fixed142
-rw-r--r--tests/ui/match_same_arms.rs20
-rw-r--r--tests/ui/match_same_arms.stderr137
-rw-r--r--tests/ui/match_same_arms2.fixed53
-rw-r--r--tests/ui/match_same_arms2.rs49
-rw-r--r--tests/ui/match_same_arms2.stderr255
-rw-r--r--tests/ui/match_same_arms_non_exhaustive.fixed24
-rw-r--r--tests/ui/match_same_arms_non_exhaustive.rs19
-rw-r--r--tests/ui/match_same_arms_non_exhaustive.stderr86
-rw-r--r--tests/ui/same_functions_in_if_condition.rs12
-rw-r--r--tests/ui/same_functions_in_if_condition.stderr67
18 files changed, 745 insertions, 397 deletions
diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs
index 42fbe6438d4..48bf3dafa6e 100644
--- a/clippy_lints/src/copies.rs
+++ b/clippy_lints/src/copies.rs
@@ -1,5 +1,5 @@
 use clippy_config::Conf;
-use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_note, span_lint_and_then};
 use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet};
 use clippy_utils::ty::{InteriorMut, needs_ordered_drop};
 use clippy_utils::visitors::for_each_expr_without_closures;
@@ -567,7 +567,7 @@ fn method_caller_is_mutable<'tcx>(
 
 /// Implementation of `IFS_SAME_COND`.
 fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mut: &mut InteriorMut<'tcx>) {
-    for (i, j) in search_same(
+    for group in search_same(
         conds,
         |e| hash_expr(cx, e),
         |lhs, rhs| {
@@ -584,14 +584,8 @@ fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mu
             }
         },
     ) {
-        span_lint_and_note(
-            cx,
-            IFS_SAME_COND,
-            j.span,
-            "this `if` has the same condition as a previous `if`",
-            Some(i.span),
-            "same as this",
-        );
+        let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect();
+        span_lint(cx, IFS_SAME_COND, spans, "these `if` branches have the same condition");
     }
 }
 
@@ -609,14 +603,13 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
         SpanlessEq::new(cx).eq_expr(lhs, rhs)
     };
 
-    for (i, j) in search_same(conds, |e| hash_expr(cx, e), eq) {
-        span_lint_and_note(
+    for group in search_same(conds, |e| hash_expr(cx, e), eq) {
+        let spans: Vec<_> = group.into_iter().map(|expr| expr.span).collect();
+        span_lint(
             cx,
             SAME_FUNCTIONS_IN_IF_CONDITION,
-            j.span,
-            "this `if` has the same function call as a previous `if`",
-            Some(i.span),
-            "same as this",
+            spans,
+            "these `if` branches have the same function call",
         );
     }
 }
diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs
index 250f17fa902..6de5b5efef0 100644
--- a/clippy_lints/src/matches/match_same_arms.rs
+++ b/clippy_lints/src/matches/match_same_arms.rs
@@ -1,8 +1,9 @@
-use clippy_utils::diagnostics::span_lint_hir_and_then;
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{SpanlessEq, SpanlessHash, is_lint_allowed, path_to_local, search_same};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::SpanRangeExt;
+use clippy_utils::{SpanlessEq, SpanlessHash, fulfill_or_allowed, is_lint_allowed, path_to_local, search_same};
 use core::cmp::Ordering;
 use core::{iter, slice};
+use itertools::Itertools;
 use rustc_arena::DroplessArena;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -110,57 +111,68 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
             && check_same_body()
     };
 
-    let mut appl = Applicability::MaybeIncorrect;
     let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
-    for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) {
-        if matches!(arm2.pat.kind, PatKind::Wild) {
-            if !cx.tcx.features().non_exhaustive_omitted_patterns_lint()
-                || is_lint_allowed(cx, NON_EXHAUSTIVE_OMITTED_PATTERNS, arm2.hir_id)
-            {
-                let arm_span = adjusted_arm_span(cx, arm1.span);
-                span_lint_hir_and_then(
-                    cx,
-                    MATCH_SAME_ARMS,
-                    arm1.hir_id,
-                    arm_span,
-                    "this match arm has an identical body to the `_` wildcard arm",
-                    |diag| {
-                        diag.span_suggestion(arm_span, "try removing the arm", "", appl)
-                            .help("or try changing either arm body")
-                            .span_note(arm2.span, "`_` wildcard arm here");
-                    },
-                );
-            }
-        } else {
-            let back_block = backwards_blocking_idxs[j];
-            let (keep_arm, move_arm) = if back_block < i || (back_block == 0 && forwards_blocking_idxs[i] <= j) {
-                (arm1, arm2)
-            } else {
-                (arm2, arm1)
-            };
-
-            span_lint_hir_and_then(
-                cx,
-                MATCH_SAME_ARMS,
-                keep_arm.hir_id,
-                keep_arm.span,
-                "this match arm has an identical body to another arm",
-                |diag| {
-                    let move_pat_snip = snippet_with_applicability(cx, move_arm.pat.span, "<pat2>", &mut appl);
-                    let keep_pat_snip = snippet_with_applicability(cx, keep_arm.pat.span, "<pat1>", &mut appl);
+    for mut group in search_same(&indexed_arms, hash, eq) {
+        // Filter out (and fulfill) `#[allow]`ed and `#[expect]`ed arms
+        group.retain(|(_, arm)| !fulfill_or_allowed(cx, MATCH_SAME_ARMS, [arm.hir_id]));
 
-                    diag.multipart_suggestion(
-                        "or try merging the arm patterns and removing the obsolete arm",
-                        vec![
-                            (keep_arm.pat.span, format!("{keep_pat_snip} | {move_pat_snip}")),
-                            (adjusted_arm_span(cx, move_arm.span), String::new()),
-                        ],
-                        appl,
-                    )
-                    .help("try changing either arm body");
-                },
-            );
+        if group.len() < 2 {
+            continue;
         }
+
+        span_lint_and_then(
+            cx,
+            MATCH_SAME_ARMS,
+            group.iter().map(|(_, arm)| arm.span).collect_vec(),
+            "these match arms have identical bodies",
+            |diag| {
+                diag.help("if this is unintentional make the arms return different values");
+
+                if let [prev @ .., (_, last)] = group.as_slice()
+                    && is_wildcard_arm(last.pat)
+                    && is_lint_allowed(cx, NON_EXHAUSTIVE_OMITTED_PATTERNS, last.hir_id)
+                {
+                    diag.span_label(last.span, "the wildcard arm");
+
+                    let s = if prev.len() > 1 { "s" } else { "" };
+                    diag.multipart_suggestion_verbose(
+                        format!("otherwise remove the non-wildcard arm{s}"),
+                        prev.iter()
+                            .map(|(_, arm)| (adjusted_arm_span(cx, arm.span), String::new()))
+                            .collect(),
+                        Applicability::MaybeIncorrect,
+                    );
+                } else if let &[&(first_idx, _), .., &(last_idx, _)] = group.as_slice() {
+                    let back_block = backwards_blocking_idxs[last_idx];
+                    let split = if back_block < first_idx
+                        || (back_block == 0 && forwards_blocking_idxs[first_idx] <= last_idx)
+                    {
+                        group.split_first()
+                    } else {
+                        group.split_last()
+                    };
+
+                    if let Some(((_, dest), src)) = split
+                        && let Some(pat_snippets) = group
+                            .iter()
+                            .map(|(_, arm)| arm.pat.span.get_source_text(cx))
+                            .collect::<Option<Vec<_>>>()
+                    {
+                        let mut suggs = src
+                            .iter()
+                            .map(|(_, arm)| (adjusted_arm_span(cx, arm.span), String::new()))
+                            .collect_vec();
+
+                        suggs.push((dest.pat.span, pat_snippets.iter().join(" | ")));
+                        diag.multipart_suggestion_verbose(
+                            "otherwise merge the patterns into a single arm",
+                            suggs,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+            },
+        );
     }
 }
 
@@ -449,3 +461,11 @@ fn bindings_eq(pat: &Pat<'_>, mut ids: HirIdSet) -> bool {
     pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.swap_remove(&id));
     result && ids.is_empty()
 }
+
+fn is_wildcard_arm(pat: &Pat<'_>) -> bool {
+    match pat.kind {
+        PatKind::Wild => true,
+        PatKind::Or([.., last]) => matches!(last.kind, PatKind::Wild),
+        _ => false,
+    }
+}
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index 551266da029..cf31cc5281e 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -27,6 +27,7 @@
 
 // FIXME: switch to something more ergonomic here, once available.
 // (Currently there is no way to opt into sysroot crates without `extern crate`.)
+extern crate indexmap;
 extern crate rustc_abi;
 extern crate rustc_ast;
 extern crate rustc_attr_parsing;
@@ -85,7 +86,6 @@ pub use self::hir_utils::{
 use core::mem;
 use core::ops::ControlFlow;
 use std::collections::hash_map::Entry;
-use std::hash::BuildHasherDefault;
 use std::iter::{once, repeat_n};
 use std::sync::{Mutex, MutexGuard, OnceLock};
 
@@ -95,7 +95,7 @@ use rustc_ast::ast::{self, LitKind, RangeLimits};
 use rustc_attr_parsing::{AttributeKind, find_attr};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::packed::Pu128;
-use rustc_data_structures::unhash::UnhashMap;
+use rustc_data_structures::unhash::UnindexMap;
 use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId};
@@ -2486,45 +2486,46 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<S
     None
 }
 
-/// Returns list of all pairs `(a, b)` where `eq(a, b) == true`
-/// and `a` is before `b` in `exprs` for all `a` and `b` in
-/// `exprs`
+/// Returns a list of groups where elements in each group are equal according to `eq`
+///
+/// - Within each group the elements are sorted by the order they appear in `exprs`
+/// - The groups themselves are sorted by their first element's appearence in `exprs`
 ///
 /// Given functions `eq` and `hash` such that `eq(a, b) == true`
 /// implies `hash(a) == hash(b)`
-pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<(&T, &T)>
+pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<Vec<&T>>
 where
     Hash: FnMut(&T) -> u64,
     Eq: FnMut(&T, &T) -> bool,
 {
     match exprs {
-        [a, b] if eq(a, b) => return vec![(a, b)],
+        [a, b] if eq(a, b) => return vec![vec![a, b]],
         _ if exprs.len() <= 2 => return vec![],
         _ => {},
     }
 
-    let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
-
-    let mut map: UnhashMap<u64, Vec<&_>> =
-        UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
+    let mut buckets: UnindexMap<u64, Vec<Vec<&T>>> = UnindexMap::default();
 
     for expr in exprs {
-        match map.entry(hash(expr)) {
-            Entry::Occupied(mut o) => {
-                for o in o.get() {
-                    if eq(o, expr) {
-                        match_expr_list.push((o, expr));
-                    }
+        match buckets.entry(hash(expr)) {
+            indexmap::map::Entry::Occupied(mut o) => {
+                let bucket = o.get_mut();
+                match bucket.iter_mut().find(|group| eq(expr, group[0])) {
+                    Some(group) => group.push(expr),
+                    None => bucket.push(vec![expr]),
                 }
-                o.get_mut().push(expr);
             },
-            Entry::Vacant(v) => {
-                v.insert(vec![expr]);
+            indexmap::map::Entry::Vacant(v) => {
+                v.insert(vec![vec![expr]]);
             },
         }
     }
 
-    match_expr_list
+    buckets
+        .into_values()
+        .flatten()
+        .filter(|group| group.len() > 1)
+        .collect()
 }
 
 /// Peels off all references on the pattern. Returns the underlying pattern and the number of
diff --git a/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs b/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs
index e953a2a4e90..2a6097fb579 100644
--- a/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs
+++ b/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs
@@ -11,9 +11,9 @@ fn issue10272() {
     // should trigger warning
     let x = Cell::new(true);
     if x.get() {
+        //~^ ifs_same_cond
     } else if !x.take() {
     } else if x.get() {
-        //~^ ifs_same_cond
     } else {
     }
 }
diff --git a/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr b/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr
index d67e7fca656..adc44358c4c 100644
--- a/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr
+++ b/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr
@@ -1,14 +1,12 @@
-error: this `if` has the same condition as a previous `if`
-  --> tests/ui-toml/ifs_same_cond/ifs_same_cond.rs:15:15
-   |
-LL |     } else if x.get() {
-   |               ^^^^^^^
-   |
-note: same as this
+error: these `if` branches have the same condition
   --> tests/ui-toml/ifs_same_cond/ifs_same_cond.rs:13:8
    |
 LL |     if x.get() {
    |        ^^^^^^^
+...
+LL |     } else if x.get() {
+   |               ^^^^^^^
+   |
    = note: `-D clippy::ifs-same-cond` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::ifs_same_cond)]`
 
diff --git a/tests/ui/ifs_same_cond.rs b/tests/ui/ifs_same_cond.rs
index ebc3acb1b77..7067434953d 100644
--- a/tests/ui/ifs_same_cond.rs
+++ b/tests/ui/ifs_same_cond.rs
@@ -6,19 +6,25 @@ fn ifs_same_cond() {
     let b = false;
 
     if b {
+        //~^ ifs_same_cond
     } else if b {
+    }
+
+    if b {
         //~^ ifs_same_cond
+    } else if b {
+    } else if b {
     }
 
     if a == 1 {
-    } else if a == 1 {
         //~^ ifs_same_cond
+    } else if a == 1 {
     }
 
     if 2 * a == 1 {
+        //~^ ifs_same_cond
     } else if 2 * a == 2 {
     } else if 2 * a == 1 {
-        //~^ ifs_same_cond
     } else if a == 1 {
     }
 
@@ -50,8 +56,8 @@ fn ifs_same_cond() {
 fn issue10272() {
     let a = String::from("ha");
     if a.contains("ah") {
-    } else if a.contains("ah") {
         //~^ ifs_same_cond
+    } else if a.contains("ah") {
 
         // Trigger this lint
     } else if a.contains("ha") {
diff --git a/tests/ui/ifs_same_cond.stderr b/tests/ui/ifs_same_cond.stderr
index df21e6f1b82..7acbc1a6399 100644
--- a/tests/ui/ifs_same_cond.stderr
+++ b/tests/ui/ifs_same_cond.stderr
@@ -1,52 +1,52 @@
-error: this `if` has the same condition as a previous `if`
-  --> tests/ui/ifs_same_cond.rs:9:15
-   |
-LL |     } else if b {
-   |               ^
-   |
-note: same as this
+error: these `if` branches have the same condition
   --> tests/ui/ifs_same_cond.rs:8:8
    |
 LL |     if b {
    |        ^
+LL |
+LL |     } else if b {
+   |               ^
+   |
    = note: `-D clippy::ifs-same-cond` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::ifs_same_cond)]`
 
-error: this `if` has the same condition as a previous `if`
-  --> tests/ui/ifs_same_cond.rs:14:15
-   |
-LL |     } else if a == 1 {
-   |               ^^^^^^
-   |
-note: same as this
+error: these `if` branches have the same condition
   --> tests/ui/ifs_same_cond.rs:13:8
    |
+LL |     if b {
+   |        ^
+LL |
+LL |     } else if b {
+   |               ^
+LL |     } else if b {
+   |               ^
+
+error: these `if` branches have the same condition
+  --> tests/ui/ifs_same_cond.rs:19:8
+   |
 LL |     if a == 1 {
    |        ^^^^^^
+LL |
+LL |     } else if a == 1 {
+   |               ^^^^^^
 
-error: this `if` has the same condition as a previous `if`
-  --> tests/ui/ifs_same_cond.rs:20:15
-   |
-LL |     } else if 2 * a == 1 {
-   |               ^^^^^^^^^^
-   |
-note: same as this
-  --> tests/ui/ifs_same_cond.rs:18:8
+error: these `if` branches have the same condition
+  --> tests/ui/ifs_same_cond.rs:24:8
    |
 LL |     if 2 * a == 1 {
    |        ^^^^^^^^^^
+...
+LL |     } else if 2 * a == 1 {
+   |               ^^^^^^^^^^
 
-error: this `if` has the same condition as a previous `if`
-  --> tests/ui/ifs_same_cond.rs:53:15
-   |
-LL |     } else if a.contains("ah") {
-   |               ^^^^^^^^^^^^^^^^
-   |
-note: same as this
-  --> tests/ui/ifs_same_cond.rs:52:8
+error: these `if` branches have the same condition
+  --> tests/ui/ifs_same_cond.rs:58:8
    |
 LL |     if a.contains("ah") {
    |        ^^^^^^^^^^^^^^^^
+LL |
+LL |     } else if a.contains("ah") {
+   |               ^^^^^^^^^^^^^^^^
 
-error: aborting due to 4 previous errors
+error: aborting due to 5 previous errors
 
diff --git a/tests/ui/match_same_arms.fixed b/tests/ui/match_same_arms.fixed
new file mode 100644
index 00000000000..31684a5759f
--- /dev/null
+++ b/tests/ui/match_same_arms.fixed
@@ -0,0 +1,142 @@
+#![allow(clippy::manual_range_patterns)]
+#![warn(clippy::match_same_arms)]
+
+pub enum Abc {
+    A,
+    B,
+    C,
+}
+
+fn match_same_arms() {
+    let _ = match Abc::A {
+        Abc::B => 1,
+        _ => 0,
+        //~^ match_same_arms
+    };
+
+    match 0 {
+        1 => 'a',
+        _ => 'b',
+        //~^ match_same_arms
+    };
+
+    match (1, 2, 3) {
+        (1, .., 3) | (.., 3) => 42,
+        //~^ match_same_arms
+        _ => 0,
+    };
+
+    let _ = match 42 {
+        //~^ match_same_arms
+        42 | 51 => 1,
+        41 | 52 => 2,
+        //~^ match_same_arms
+        _ => 0,
+    };
+
+    let _ = match 42 {
+        //~^ match_same_arms
+        1 | 2 | 3 => 2,
+        4 => 3,
+        _ => 0,
+    };
+}
+
+mod issue4244 {
+    #[derive(PartialEq, PartialOrd, Eq, Ord)]
+    pub enum CommandInfo {
+        BuiltIn { name: String, about: Option<String> },
+        External { name: String, path: std::path::PathBuf },
+    }
+
+    impl CommandInfo {
+        pub fn name(&self) -> String {
+            match self {
+                //~^ match_same_arms
+                CommandInfo::BuiltIn { name, .. } | CommandInfo::External { name, .. } => name.to_string(),
+            }
+        }
+    }
+}
+
+macro_rules! m {
+    (foo) => {};
+    (bar) => {};
+}
+macro_rules! foo {
+    () => {
+        1
+    };
+}
+macro_rules! bar {
+    () => {
+        1
+    };
+}
+
+fn main() {
+    let x = 0;
+    let _ = match 0 {
+        0 => {
+            m!(foo);
+            x
+        },
+        1 => {
+            m!(bar);
+            x
+        },
+        _ => 1,
+    };
+
+    let _ = match 0 {
+        0 => {
+            m!(foo);
+            0
+        },
+        1 => {
+            m!(bar);
+            0
+        },
+        _ => 1,
+    };
+
+    let _ = match 0 {
+        0 => {
+            let mut x = 0;
+            #[cfg(not_enabled)]
+            {
+                x = 5;
+            }
+            #[cfg(not(not_enabled))]
+            {
+                x = 6;
+            }
+            x
+        },
+        1 => {
+            let mut x = 0;
+            #[cfg(also_not_enabled)]
+            {
+                x = 5;
+            }
+            #[cfg(not(also_not_enabled))]
+            {
+                x = 6;
+            }
+            x
+        },
+        _ => 0,
+    };
+
+    let _ = match 0 {
+        0 => foo!(),
+        1 => bar!(),
+        _ => 1,
+    };
+
+    let _ = match 0 {
+        0 => cfg!(not_enabled),
+        1 => cfg!(also_not_enabled),
+        _ => false,
+    };
+}
diff --git a/tests/ui/match_same_arms.rs b/tests/ui/match_same_arms.rs
index 55441948e91..39bee01bac2 100644
--- a/tests/ui/match_same_arms.rs
+++ b/tests/ui/match_same_arms.rs
@@ -1,4 +1,4 @@
-//@no-rustfix: overlapping suggestions
+#![allow(clippy::manual_range_patterns)]
 #![warn(clippy::match_same_arms)]
 
 pub enum Abc {
@@ -10,9 +10,17 @@ pub enum Abc {
 fn match_same_arms() {
     let _ = match Abc::A {
         Abc::A => 0,
-        //~^ match_same_arms
         Abc::B => 1,
         _ => 0,
+        //~^ match_same_arms
+    };
+
+    match 0 {
+        1 => 'a',
+        2 => 'b',
+        3 => 'b',
+        _ => 'b',
+        //~^ match_same_arms
     };
 
     match (1, 2, 3) {
@@ -24,8 +32,8 @@ fn match_same_arms() {
 
     let _ = match 42 {
         42 => 1,
-        51 => 1,
         //~^ match_same_arms
+        51 => 1,
         41 => 2,
         //~^ match_same_arms
         52 => 2,
@@ -34,11 +42,9 @@ fn match_same_arms() {
 
     let _ = match 42 {
         1 => 2,
-        2 => 2,
         //~^ match_same_arms
-        //~| match_same_arms
+        2 => 2,
         3 => 2,
-        //~^ match_same_arms
         4 => 3,
         _ => 0,
     };
@@ -55,8 +61,8 @@ mod issue4244 {
         pub fn name(&self) -> String {
             match self {
                 CommandInfo::BuiltIn { name, .. } => name.to_string(),
-                CommandInfo::External { name, .. } => name.to_string(),
                 //~^ match_same_arms
+                CommandInfo::External { name, .. } => name.to_string(),
             }
         }
     }
diff --git a/tests/ui/match_same_arms.stderr b/tests/ui/match_same_arms.stderr
index 3744b83d89c..8aa60f83576 100644
--- a/tests/ui/match_same_arms.stderr
+++ b/tests/ui/match_same_arms.stderr
@@ -1,118 +1,121 @@
-error: this match arm has an identical body to the `_` wildcard arm
+error: these match arms have identical bodies
   --> tests/ui/match_same_arms.rs:12:9
    |
-LL | /         Abc::A => 0,
-LL | |
-   | |________^ help: try removing the arm
-   |
-   = help: or try changing either arm body
-note: `_` wildcard arm here
-  --> tests/ui/match_same_arms.rs:15:9
-   |
+LL |         Abc::A => 0,
+   |         ^^^^^^^^^^^
+LL |         Abc::B => 1,
 LL |         _ => 0,
-   |         ^^^^^^
+   |         ^^^^^^ the wildcard arm
+   |
+   = help: if this is unintentional make the arms return different values
    = note: `-D clippy::match-same-arms` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]`
+help: otherwise remove the non-wildcard arm
+   |
+LL -         Abc::A => 0,
+   |
 
-error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms.rs:19:9
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms.rs:20:9
+   |
+LL |         2 => 'b',
+   |         ^^^^^^^^
+LL |         3 => 'b',
+   |         ^^^^^^^^
+LL |         _ => 'b',
+   |         ^^^^^^^^ the wildcard arm
+   |
+   = help: if this is unintentional make the arms return different values
+help: otherwise remove the non-wildcard arms
+   |
+LL -         2 => 'b',
+LL -         3 => 'b',
+LL +         _ => 'b',
+   |
+
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms.rs:27:9
    |
 LL |         (1, .., 3) => 42,
    |         ^^^^^^^^^^^^^^^^
+LL |
+LL |         (.., 3) => 42,
+   |         ^^^^^^^^^^^^^
    |
-   = help: try changing either arm body
-help: or try merging the arm patterns and removing the obsolete arm
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
    |
 LL ~         (1, .., 3) | (.., 3) => 42,
 LL |
 LL ~         _ => 0,
    |
 
-error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms.rs:27:9
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms.rs:34:9
    |
+LL |         42 => 1,
+   |         ^^^^^^^
+LL |
 LL |         51 => 1,
    |         ^^^^^^^
    |
-   = help: try changing either arm body
-help: or try merging the arm patterns and removing the obsolete arm
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
    |
-LL -         42 => 1,
-LL -         51 => 1,
-LL +         51 | 42 => 1,
+LL ~
+LL ~         42 | 51 => 1,
    |
 
-error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms.rs:29:9
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms.rs:37:9
    |
 LL |         41 => 2,
    |         ^^^^^^^
+LL |
+LL |         52 => 2,
+   |         ^^^^^^^
    |
-   = help: try changing either arm body
-help: or try merging the arm patterns and removing the obsolete arm
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
    |
 LL ~         41 | 52 => 2,
 LL |
 LL ~         _ => 0,
    |
 
-error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms.rs:37:9
-   |
-LL |         2 => 2,
-   |         ^^^^^^
-   |
-   = help: try changing either arm body
-help: or try merging the arm patterns and removing the obsolete arm
-   |
-LL -         1 => 2,
-LL -         2 => 2,
-LL +         2 | 1 => 2,
-   |
-
-error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms.rs:40:9
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms.rs:44:9
    |
-LL |         3 => 2,
+LL |         1 => 2,
    |         ^^^^^^
-   |
-   = help: try changing either arm body
-help: or try merging the arm patterns and removing the obsolete arm
-   |
-LL ~         2 => 2,
-LL |
 LL |
-LL ~         3 | 1 => 2,
-   |
-
-error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms.rs:37:9
-   |
 LL |         2 => 2,
    |         ^^^^^^
+LL |         3 => 2,
+   |         ^^^^^^
    |
-   = help: try changing either arm body
-help: or try merging the arm patterns and removing the obsolete arm
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
    |
-LL ~         2 | 3 => 2,
-LL |
-LL |
 LL ~
+LL ~         1 | 2 | 3 => 2,
    |
 
-error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms.rs:58:17
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms.rs:63:17
    |
+LL |                 CommandInfo::BuiltIn { name, .. } => name.to_string(),
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
 LL |                 CommandInfo::External { name, .. } => name.to_string(),
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: try changing either arm body
-help: or try merging the arm patterns and removing the obsolete arm
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
    |
-LL -                 CommandInfo::BuiltIn { name, .. } => name.to_string(),
-LL -                 CommandInfo::External { name, .. } => name.to_string(),
-LL +                 CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. } => name.to_string(),
+LL ~
+LL ~                 CommandInfo::BuiltIn { name, .. } | CommandInfo::External { name, .. } => name.to_string(),
    |
 
-error: aborting due to 8 previous errors
+error: aborting due to 7 previous errors
 
diff --git a/tests/ui/match_same_arms2.fixed b/tests/ui/match_same_arms2.fixed
index 0d93e2c728d..cb860cef1e6 100644
--- a/tests/ui/match_same_arms2.fixed
+++ b/tests/ui/match_same_arms2.fixed
@@ -14,7 +14,7 @@ fn foo() -> bool {
 
 fn match_same_arms() {
     let _ = match 42 {
-        //~^^^^^^^^^ match_same_arms
+        //~v match_same_arms
         _ => {
             foo();
             let mut a = 42 + [23].len() as i32;
@@ -27,14 +27,14 @@ fn match_same_arms() {
     };
 
     let _ = match 42 {
-        51 | 42 => foo(),
         //~^ match_same_arms
+        42 | 51 => foo(),
         _ => true,
     };
 
     let _ = match Some(42) {
-        None | Some(_) => 24,
         //~^ match_same_arms
+        Some(_) | None => 24,
     };
 
     let _ = match Some(42) {
@@ -55,8 +55,8 @@ fn match_same_arms() {
     };
 
     match (Some(42), Some(42)) {
-        (None, Some(a)) | (Some(a), None) => bar(a),
         //~^ match_same_arms
+        (Some(a), None) | (None, Some(a)) => bar(a),
         _ => (),
     }
 
@@ -69,8 +69,8 @@ fn match_same_arms() {
     };
 
     let _ = match (Some(42), Some(42)) {
-        (None, Some(a)) | (Some(a), None) if a == 42 => a,
         //~^ match_same_arms
+        (Some(a), None) | (None, Some(a)) if a == 42 => a,
         _ => 0,
     };
 
@@ -124,8 +124,8 @@ fn match_same_arms() {
     // False negative #2251.
     match x {
         Ok(_tmp) => println!("ok"),
-        Ok(_) | Ok(3) => println!("ok"),
         //~^ match_same_arms
+        Ok(3) | Ok(_) => println!("ok"),
         Err(_) => {
             unreachable!();
         },
@@ -149,10 +149,10 @@ fn match_same_arms() {
 
     // still lint if the tokens are the same
     match 0 {
-        1 | 0 => {
+        //~^^^ match_same_arms
+        0 | 1 => {
             empty!(0);
         },
-        //~^^^ match_same_arms
         x => {
             empty!(x);
         },
@@ -208,9 +208,9 @@ fn main() {
 
     // Suggest moving `Foo::X(0)` down.
     let _ = match Foo::X(0) {
-        Foo::Y(_) | Foo::Z(0) => 2,
-        Foo::Z(_) | Foo::X(0) => 1,
         //~^ match_same_arms
+        Foo::Y(_) | Foo::Z(0) => 2,
+        Foo::X(0) | Foo::Z(_) => 1,
         _ => 0,
     };
 
@@ -230,10 +230,10 @@ fn main() {
 
     // Lint.
     let _ = match None {
+        //~^ match_same_arms
         Some(Bar { y: 10, z: 0, .. }) => 2,
         None => 50,
-        Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1,
-        //~^ match_same_arms
+        Some(Bar { x: 0, y: 5, .. }) | Some(Bar { y: 0, x: 5, .. }) => 1,
         _ => 200,
     };
 
@@ -246,8 +246,8 @@ fn main() {
     };
 
     let _ = match 0 {
-        1 | 0 => cfg!(not_enable),
         //~^ match_same_arms
+        0 | 1 => cfg!(not_enable),
         _ => false,
     };
 }
@@ -262,9 +262,34 @@ mod with_lifetime {
     impl<'a> MaybeStaticStr<'a> {
         fn get(&self) -> &'a str {
             match *self {
-                MaybeStaticStr::Borrowed(s) | MaybeStaticStr::Static(s) => s,
                 //~^ match_same_arms
+                MaybeStaticStr::Static(s) | MaybeStaticStr::Borrowed(s) => s,
             }
         }
     }
 }
+
+fn lint_levels() {
+    match 1 {
+        0 => "a",
+        1 => "b",
+        #[expect(clippy::match_same_arms)]
+        _ => "b",
+    };
+
+    match 2 {
+        0 => "a",
+        1 | 2 => "b",
+        //~^ match_same_arms
+        #[allow(clippy::match_same_arms)]
+        _ => "b",
+    };
+
+    match 3 {
+        0 => "a",
+        1 | 2 => "b",
+        //~^ match_same_arms
+        #[expect(clippy::match_same_arms)]
+        _ => "b",
+    };
+}
diff --git a/tests/ui/match_same_arms2.rs b/tests/ui/match_same_arms2.rs
index b0ebc4784f3..0fd5d76e7d3 100644
--- a/tests/ui/match_same_arms2.rs
+++ b/tests/ui/match_same_arms2.rs
@@ -23,7 +23,7 @@ fn match_same_arms() {
             a = -31 - a;
             a
         },
-        //~^^^^^^^^^ match_same_arms
+        //~v match_same_arms
         _ => {
             foo();
             let mut a = 42 + [23].len() as i32;
@@ -37,15 +37,15 @@ fn match_same_arms() {
 
     let _ = match 42 {
         42 => foo(),
-        51 => foo(),
         //~^ match_same_arms
+        51 => foo(),
         _ => true,
     };
 
     let _ = match Some(42) {
         Some(_) => 24,
-        None => 24,
         //~^ match_same_arms
+        None => 24,
     };
 
     let _ = match Some(42) {
@@ -67,8 +67,8 @@ fn match_same_arms() {
 
     match (Some(42), Some(42)) {
         (Some(a), None) => bar(a),
-        (None, Some(a)) => bar(a),
         //~^ match_same_arms
+        (None, Some(a)) => bar(a),
         _ => (),
     }
 
@@ -82,8 +82,8 @@ fn match_same_arms() {
 
     let _ = match (Some(42), Some(42)) {
         (Some(a), None) if a == 42 => a,
-        (None, Some(a)) if a == 42 => a,
         //~^ match_same_arms
+        (None, Some(a)) if a == 42 => a,
         _ => 0,
     };
 
@@ -140,8 +140,8 @@ fn match_same_arms() {
     match x {
         Ok(_tmp) => println!("ok"),
         Ok(3) => println!("ok"),
-        Ok(_) => println!("ok"),
         //~^ match_same_arms
+        Ok(_) => println!("ok"),
         Err(_) => {
             unreachable!();
         },
@@ -168,10 +168,10 @@ fn match_same_arms() {
         0 => {
             empty!(0);
         },
+        //~^^^ match_same_arms
         1 => {
             empty!(0);
         },
-        //~^^^ match_same_arms
         x => {
             empty!(x);
         },
@@ -229,9 +229,9 @@ fn main() {
     // Suggest moving `Foo::X(0)` down.
     let _ = match Foo::X(0) {
         Foo::X(0) => 1,
+        //~^ match_same_arms
         Foo::Y(_) | Foo::Z(0) => 2,
         Foo::Z(_) => 1,
-        //~^ match_same_arms
         _ => 0,
     };
 
@@ -252,10 +252,10 @@ fn main() {
     // Lint.
     let _ = match None {
         Some(Bar { x: 0, y: 5, .. }) => 1,
+        //~^ match_same_arms
         Some(Bar { y: 10, z: 0, .. }) => 2,
         None => 50,
         Some(Bar { y: 0, x: 5, .. }) => 1,
-        //~^ match_same_arms
         _ => 200,
     };
 
@@ -269,8 +269,8 @@ fn main() {
 
     let _ = match 0 {
         0 => cfg!(not_enable),
-        1 => cfg!(not_enable),
         //~^ match_same_arms
+        1 => cfg!(not_enable),
         _ => false,
     };
 }
@@ -286,9 +286,36 @@ mod with_lifetime {
         fn get(&self) -> &'a str {
             match *self {
                 MaybeStaticStr::Static(s) => s,
-                MaybeStaticStr::Borrowed(s) => s,
                 //~^ match_same_arms
+                MaybeStaticStr::Borrowed(s) => s,
             }
         }
     }
 }
+
+fn lint_levels() {
+    match 1 {
+        0 => "a",
+        1 => "b",
+        #[expect(clippy::match_same_arms)]
+        _ => "b",
+    };
+
+    match 2 {
+        0 => "a",
+        1 => "b",
+        //~^ match_same_arms
+        2 => "b",
+        #[allow(clippy::match_same_arms)]
+        _ => "b",
+    };
+
+    match 3 {
+        0 => "a",
+        1 => "b",
+        //~^ match_same_arms
+        2 => "b",
+        #[expect(clippy::match_same_arms)]
+        _ => "b",
+    };
+}
diff --git a/tests/ui/match_same_arms2.stderr b/tests/ui/match_same_arms2.stderr
index 21a8743cc32..f3031619cce 100644
--- a/tests/ui/match_same_arms2.stderr
+++ b/tests/ui/match_same_arms2.stderr
@@ -1,4 +1,4 @@
-error: this match arm has an identical body to the `_` wildcard arm
+error: these match arms have identical bodies
   --> tests/ui/match_same_arms2.rs:17:9
    |
 LL | /         42 => {
@@ -6,14 +6,10 @@ LL | |             foo();
 LL | |             let mut a = 42 + [23].len() as i32;
 LL | |             if true {
 ...  |
+LL | |             a
 LL | |         },
-LL | |
-   | |________^ help: try removing the arm
-   |
-   = help: or try changing either arm body
-note: `_` wildcard arm here
-  --> tests/ui/match_same_arms2.rs:27:9
-   |
+   | |_________^
+LL |
 LL | /         _ => {
 LL | |             foo();
 LL | |             let mut a = 42 + [23].len() as i32;
@@ -21,134 +17,169 @@ LL | |             if true {
 ...  |
 LL | |             a
 LL | |         },
-   | |_________^
+   | |_________^ the wildcard arm
+   |
+   = help: if this is unintentional make the arms return different values
    = note: `-D clippy::match-same-arms` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]`
+help: otherwise remove the non-wildcard arm
+   |
+LL -         42 => {
+LL -             foo();
+LL -             let mut a = 42 + [23].len() as i32;
+LL -             if true {
+LL -                 a += 7;
+LL -             }
+LL -             a = -31 - a;
+LL -             a
+LL -         },
+   |
 
-error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:40:9
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms2.rs:39:9
    |
+LL |         42 => foo(),
+   |         ^^^^^^^^^^^
+LL |
 LL |         51 => foo(),
    |         ^^^^^^^^^^^
    |
-   = help: try changing either arm body
-help: or try merging the arm patterns and removing the obsolete arm
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
    |
-LL -         42 => foo(),
-LL -         51 => foo(),
-LL +         51 | 42 => foo(),
+LL ~
+LL ~         42 | 51 => foo(),
    |
 
-error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:47:9
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms2.rs:46:9
    |
+LL |         Some(_) => 24,
+   |         ^^^^^^^^^^^^^
+LL |
 LL |         None => 24,
    |         ^^^^^^^^^^
    |
-   = help: try changing either arm body
-help: or try merging the arm patterns and removing the obsolete arm
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
    |
-LL -         Some(_) => 24,
-LL -         None => 24,
-LL +         None | Some(_) => 24,
+LL ~
+LL ~         Some(_) | None => 24,
    |
 
-error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:70:9
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms2.rs:69:9
    |
+LL |         (Some(a), None) => bar(a),
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
 LL |         (None, Some(a)) => bar(a),
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: try changing either arm body
-help: or try merging the arm patterns and removing the obsolete arm
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
    |
-LL -         (Some(a), None) => bar(a),
-LL -         (None, Some(a)) => bar(a),
-LL +         (None, Some(a)) | (Some(a), None) => bar(a),
+LL ~
+LL ~         (Some(a), None) | (None, Some(a)) => bar(a),
    |
 
-error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:85:9
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms2.rs:84:9
    |
+LL |         (Some(a), None) if a == 42 => a,
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
 LL |         (None, Some(a)) if a == 42 => a,
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: try changing either arm body
-help: or try merging the arm patterns and removing the obsolete arm
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
    |
-LL -         (Some(a), None) if a == 42 => a,
-LL -         (None, Some(a)) if a == 42 => a,
-LL +         (None, Some(a)) | (Some(a), None) if a == 42 => a,
+LL ~
+LL ~         (Some(a), None) | (None, Some(a)) if a == 42 => a,
    |
 
-error: this match arm has an identical body to another arm
+error: these match arms have identical bodies
   --> tests/ui/match_same_arms2.rs:91:9
    |
 LL |         (Some(a), ..) => bar(a),
    |         ^^^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL |         (.., Some(a)) => bar(a),
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: try changing either arm body
-help: or try merging the arm patterns and removing the obsolete arm
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
    |
 LL ~         (Some(a), ..) | (.., Some(a)) => bar(a),
 LL |
 LL ~         _ => (),
    |
 
-error: this match arm has an identical body to another arm
+error: these match arms have identical bodies
   --> tests/ui/match_same_arms2.rs:126:9
    |
 LL |         (Ok(x), Some(_)) => println!("ok {}", x),
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL |         (Ok(_), Some(x)) => println!("ok {}", x),
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: try changing either arm body
-help: or try merging the arm patterns and removing the obsolete arm
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
    |
 LL ~         (Ok(x), Some(_)) | (Ok(_), Some(x)) => println!("ok {}", x),
 LL |
 LL ~         _ => println!("err"),
    |
 
-error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:143:9
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms2.rs:142:9
    |
+LL |         Ok(3) => println!("ok"),
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+LL |
 LL |         Ok(_) => println!("ok"),
    |         ^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: try changing either arm body
-help: or try merging the arm patterns and removing the obsolete arm
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
    |
-LL -         Ok(3) => println!("ok"),
-LL -         Ok(_) => println!("ok"),
-LL +         Ok(_) | Ok(3) => println!("ok"),
+LL ~
+LL ~         Ok(3) | Ok(_) => println!("ok"),
    |
 
-error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:171:9
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms2.rs:168:9
    |
+LL | /         0 => {
+LL | |             empty!(0);
+LL | |         },
+   | |_________^
+LL |
 LL | /         1 => {
 LL | |             empty!(0);
 LL | |         },
    | |_________^
    |
-   = help: try changing either arm body
-help: or try merging the arm patterns and removing the obsolete arm
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
    |
-LL -         0 => {
-LL -             empty!(0);
-LL -         },
-LL -         1 => {
-LL +         1 | 0 => {
+LL ~
+LL ~         0 | 1 => {
    |
 
-error: this match arm has an identical body to another arm
+error: these match arms have identical bodies
   --> tests/ui/match_same_arms2.rs:222:9
    |
 LL |         Foo::X(0) => 1,
    |         ^^^^^^^^^^^^^^
+...
+LL |         Foo::Z(_) => 1,
+   |         ^^^^^^^^^^^^^^
    |
-   = help: try changing either arm body
-help: or try merging the arm patterns and removing the obsolete arm
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
    |
 LL ~         Foo::X(0) | Foo::Z(_) => 1,
 LL |
@@ -156,60 +187,106 @@ LL |         Foo::X(_) | Foo::Y(_) => 2,
 LL ~         _ => 0,
    |
 
-error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:233:9
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms2.rs:231:9
    |
+LL |         Foo::X(0) => 1,
+   |         ^^^^^^^^^^^^^^
+...
 LL |         Foo::Z(_) => 1,
    |         ^^^^^^^^^^^^^^
    |
-   = help: try changing either arm body
-help: or try merging the arm patterns and removing the obsolete arm
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
    |
-LL ~         Foo::Y(_) | Foo::Z(0) => 2,
-LL ~         Foo::Z(_) | Foo::X(0) => 1,
+LL ~
+LL |         Foo::Y(_) | Foo::Z(0) => 2,
+LL ~         Foo::X(0) | Foo::Z(_) => 1,
    |
 
-error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:257:9
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms2.rs:254:9
    |
+LL |         Some(Bar { x: 0, y: 5, .. }) => 1,
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
 LL |         Some(Bar { y: 0, x: 5, .. }) => 1,
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: try changing either arm body
-help: or try merging the arm patterns and removing the obsolete arm
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
    |
-LL ~         Some(Bar { y: 10, z: 0, .. }) => 2,
+LL ~
+LL |         Some(Bar { y: 10, z: 0, .. }) => 2,
 LL |         None => 50,
-LL ~         Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1,
+LL ~         Some(Bar { x: 0, y: 5, .. }) | Some(Bar { y: 0, x: 5, .. }) => 1,
    |
 
-error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:272:9
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms2.rs:271:9
    |
+LL |         0 => cfg!(not_enable),
+   |         ^^^^^^^^^^^^^^^^^^^^^
+LL |
 LL |         1 => cfg!(not_enable),
    |         ^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: try changing either arm body
-help: or try merging the arm patterns and removing the obsolete arm
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
    |
-LL -         0 => cfg!(not_enable),
-LL -         1 => cfg!(not_enable),
-LL +         1 | 0 => cfg!(not_enable),
+LL ~
+LL ~         0 | 1 => cfg!(not_enable),
    |
 
-error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:289:17
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms2.rs:288:17
    |
+LL |                 MaybeStaticStr::Static(s) => s,
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
 LL |                 MaybeStaticStr::Borrowed(s) => s,
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: try changing either arm body
-help: or try merging the arm patterns and removing the obsolete arm
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
+   |
+LL ~
+LL ~                 MaybeStaticStr::Static(s) | MaybeStaticStr::Borrowed(s) => s,
    |
-LL -                 MaybeStaticStr::Static(s) => s,
-LL -                 MaybeStaticStr::Borrowed(s) => s,
-LL +                 MaybeStaticStr::Borrowed(s) | MaybeStaticStr::Static(s) => s,
+
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms2.rs:306:9
+   |
+LL |         1 => "b",
+   |         ^^^^^^^^
+LL |
+LL |         2 => "b",
+   |         ^^^^^^^^
+   |
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
+   |
+LL ~         1 | 2 => "b",
+LL |
+LL ~         #[allow(clippy::match_same_arms)]
+   |
+
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms2.rs:315:9
+   |
+LL |         1 => "b",
+   |         ^^^^^^^^
+LL |
+LL |         2 => "b",
+   |         ^^^^^^^^
+   |
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
+   |
+LL ~         1 | 2 => "b",
+LL |
+LL ~         #[expect(clippy::match_same_arms)]
    |
 
-error: aborting due to 14 previous errors
+error: aborting due to 16 previous errors
 
diff --git a/tests/ui/match_same_arms_non_exhaustive.fixed b/tests/ui/match_same_arms_non_exhaustive.fixed
index 0c9398933b8..61a5bd0323a 100644
--- a/tests/ui/match_same_arms_non_exhaustive.fixed
+++ b/tests/ui/match_same_arms_non_exhaustive.fixed
@@ -7,14 +7,22 @@ fn repeat() -> ! {
     panic!()
 }
 
+#[deny(non_exhaustive_omitted_patterns)]
 pub fn f(x: Ordering) {
-    #[deny(non_exhaustive_omitted_patterns)]
     match x {
         Ordering::Relaxed => println!("relaxed"),
         Ordering::Release => println!("release"),
         Ordering::Acquire => println!("acquire"),
-        Ordering::AcqRel | Ordering::SeqCst => repeat(),
-        _ => repeat(),
+        //~^ match_same_arms
+        Ordering::AcqRel | Ordering::SeqCst | _ => repeat(),
+    }
+
+    match x {
+        Ordering::Relaxed => println!("relaxed"),
+        Ordering::Release => println!("release"),
+        Ordering::Acquire => println!("acquire"),
+        //~^ match_same_arms
+        Ordering::AcqRel | Ordering::SeqCst | _ => repeat(),
     }
 }
 
@@ -28,21 +36,21 @@ mod f {
             Ordering::Relaxed => println!("relaxed"),
             Ordering::Release => println!("release"),
             Ordering::Acquire => println!("acquire"),
-            Ordering::AcqRel | Ordering::SeqCst => repeat(),
-            _ => repeat(),
+            //~^ match_same_arms
+            Ordering::AcqRel | Ordering::SeqCst | _ => repeat(),
         }
     }
 }
 
-// Below should still lint
+// Below can still suggest removing the other patterns
 
 pub fn g(x: Ordering) {
     match x {
         Ordering::Relaxed => println!("relaxed"),
         Ordering::Release => println!("release"),
         Ordering::Acquire => println!("acquire"),
-        //~^ match_same_arms
         _ => repeat(),
+        //~^ match_same_arms
     }
 }
 
@@ -54,8 +62,8 @@ mod g {
             Ordering::Relaxed => println!("relaxed"),
             Ordering::Release => println!("release"),
             Ordering::Acquire => println!("acquire"),
-            //~^ match_same_arms
             _ => repeat(),
+            //~^ match_same_arms
         }
     }
 }
diff --git a/tests/ui/match_same_arms_non_exhaustive.rs b/tests/ui/match_same_arms_non_exhaustive.rs
index 304a9e5c28e..66f65eb39d0 100644
--- a/tests/ui/match_same_arms_non_exhaustive.rs
+++ b/tests/ui/match_same_arms_non_exhaustive.rs
@@ -7,15 +7,25 @@ fn repeat() -> ! {
     panic!()
 }
 
+#[deny(non_exhaustive_omitted_patterns)]
 pub fn f(x: Ordering) {
-    #[deny(non_exhaustive_omitted_patterns)]
     match x {
         Ordering::Relaxed => println!("relaxed"),
         Ordering::Release => println!("release"),
         Ordering::Acquire => println!("acquire"),
         Ordering::AcqRel | Ordering::SeqCst => repeat(),
+        //~^ match_same_arms
         _ => repeat(),
     }
+
+    match x {
+        Ordering::Relaxed => println!("relaxed"),
+        Ordering::Release => println!("release"),
+        Ordering::Acquire => println!("acquire"),
+        Ordering::AcqRel => repeat(),
+        //~^ match_same_arms
+        Ordering::SeqCst | _ => repeat(),
+    }
 }
 
 mod f {
@@ -29,12 +39,13 @@ mod f {
             Ordering::Release => println!("release"),
             Ordering::Acquire => println!("acquire"),
             Ordering::AcqRel | Ordering::SeqCst => repeat(),
+            //~^ match_same_arms
             _ => repeat(),
         }
     }
 }
 
-// Below should still lint
+// Below can still suggest removing the other patterns
 
 pub fn g(x: Ordering) {
     match x {
@@ -42,8 +53,8 @@ pub fn g(x: Ordering) {
         Ordering::Release => println!("release"),
         Ordering::Acquire => println!("acquire"),
         Ordering::AcqRel | Ordering::SeqCst => repeat(),
-        //~^ match_same_arms
         _ => repeat(),
+        //~^ match_same_arms
     }
 }
 
@@ -56,8 +67,8 @@ mod g {
             Ordering::Release => println!("release"),
             Ordering::Acquire => println!("acquire"),
             Ordering::AcqRel | Ordering::SeqCst => repeat(),
-            //~^ match_same_arms
             _ => repeat(),
+            //~^ match_same_arms
         }
     }
 }
diff --git a/tests/ui/match_same_arms_non_exhaustive.stderr b/tests/ui/match_same_arms_non_exhaustive.stderr
index aa7f8c95dce..03252f346c6 100644
--- a/tests/ui/match_same_arms_non_exhaustive.stderr
+++ b/tests/ui/match_same_arms_non_exhaustive.stderr
@@ -1,32 +1,80 @@
-error: this match arm has an identical body to the `_` wildcard arm
-  --> tests/ui/match_same_arms_non_exhaustive.rs:44:9
-   |
-LL | /         Ordering::AcqRel | Ordering::SeqCst => repeat(),
-LL | |
-   | |________^ help: try removing the arm
-   |
-   = help: or try changing either arm body
-note: `_` wildcard arm here
-  --> tests/ui/match_same_arms_non_exhaustive.rs:46:9
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms_non_exhaustive.rs:16:9
    |
+LL |         Ordering::AcqRel | Ordering::SeqCst => repeat(),
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
 LL |         _ => repeat(),
    |         ^^^^^^^^^^^^^
+   |
+   = help: if this is unintentional make the arms return different values
    = note: `-D clippy::match-same-arms` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]`
+help: otherwise merge the patterns into a single arm
+   |
+LL ~
+LL ~         Ordering::AcqRel | Ordering::SeqCst | _ => repeat(),
+   |
 
-error: this match arm has an identical body to the `_` wildcard arm
-  --> tests/ui/match_same_arms_non_exhaustive.rs:58:13
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms_non_exhaustive.rs:25:9
+   |
+LL |         Ordering::AcqRel => repeat(),
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL |         Ordering::SeqCst | _ => repeat(),
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-LL | /             Ordering::AcqRel | Ordering::SeqCst => repeat(),
-LL | |
-   | |____________^ help: try removing the arm
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
    |
-   = help: or try changing either arm body
-note: `_` wildcard arm here
-  --> tests/ui/match_same_arms_non_exhaustive.rs:60:13
+LL ~
+LL ~         Ordering::AcqRel | Ordering::SeqCst | _ => repeat(),
    |
+
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms_non_exhaustive.rs:41:13
+   |
+LL |             Ordering::AcqRel | Ordering::SeqCst => repeat(),
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
 LL |             _ => repeat(),
    |             ^^^^^^^^^^^^^
+   |
+   = help: if this is unintentional make the arms return different values
+help: otherwise merge the patterns into a single arm
+   |
+LL ~
+LL ~             Ordering::AcqRel | Ordering::SeqCst | _ => repeat(),
+   |
+
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms_non_exhaustive.rs:55:9
+   |
+LL |         Ordering::AcqRel | Ordering::SeqCst => repeat(),
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         _ => repeat(),
+   |         ^^^^^^^^^^^^^ the wildcard arm
+   |
+   = help: if this is unintentional make the arms return different values
+help: otherwise remove the non-wildcard arm
+   |
+LL -         Ordering::AcqRel | Ordering::SeqCst => repeat(),
+   |
+
+error: these match arms have identical bodies
+  --> tests/ui/match_same_arms_non_exhaustive.rs:69:13
+   |
+LL |             Ordering::AcqRel | Ordering::SeqCst => repeat(),
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |             _ => repeat(),
+   |             ^^^^^^^^^^^^^ the wildcard arm
+   |
+   = help: if this is unintentional make the arms return different values
+help: otherwise remove the non-wildcard arm
+   |
+LL -             Ordering::AcqRel | Ordering::SeqCst => repeat(),
+   |
 
-error: aborting due to 2 previous errors
+error: aborting due to 5 previous errors
 
diff --git a/tests/ui/same_functions_in_if_condition.rs b/tests/ui/same_functions_in_if_condition.rs
index a98b73c9e1c..3f205b322ab 100644
--- a/tests/ui/same_functions_in_if_condition.rs
+++ b/tests/ui/same_functions_in_if_condition.rs
@@ -31,34 +31,34 @@ fn ifs_same_cond_fn() {
     let obj = Struct;
 
     if function() {
-    } else if function() {
         //~^ same_functions_in_if_condition
+    } else if function() {
     }
 
     if fn_arg(a) {
-    } else if fn_arg(a) {
         //~^ same_functions_in_if_condition
+    } else if fn_arg(a) {
     }
 
     if obj.method() {
-    } else if obj.method() {
         //~^ same_functions_in_if_condition
+    } else if obj.method() {
     }
 
     if obj.method_arg(a) {
-    } else if obj.method_arg(a) {
         //~^ same_functions_in_if_condition
+    } else if obj.method_arg(a) {
     }
 
     let mut v = vec![1];
     if v.pop().is_none() {
-    } else if v.pop().is_none() {
         //~^ same_functions_in_if_condition
+    } else if v.pop().is_none() {
     }
 
     if v.len() == 42 {
-    } else if v.len() == 42 {
         //~^ same_functions_in_if_condition
+    } else if v.len() == 42 {
     }
 
     if v.len() == 1 {
diff --git a/tests/ui/same_functions_in_if_condition.stderr b/tests/ui/same_functions_in_if_condition.stderr
index 35dcbadce59..59f4511757d 100644
--- a/tests/ui/same_functions_in_if_condition.stderr
+++ b/tests/ui/same_functions_in_if_condition.stderr
@@ -1,79 +1,62 @@
-error: this `if` has the same function call as a previous `if`
-  --> tests/ui/same_functions_in_if_condition.rs:34:15
-   |
-LL |     } else if function() {
-   |               ^^^^^^^^^^
-   |
-note: same as this
+error: these `if` branches have the same function call
   --> tests/ui/same_functions_in_if_condition.rs:33:8
    |
 LL |     if function() {
    |        ^^^^^^^^^^
+LL |
+LL |     } else if function() {
+   |               ^^^^^^^^^^
+   |
 note: the lint level is defined here
   --> tests/ui/same_functions_in_if_condition.rs:2:9
    |
 LL | #![deny(clippy::same_functions_in_if_condition)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: this `if` has the same function call as a previous `if`
-  --> tests/ui/same_functions_in_if_condition.rs:39:15
-   |
-LL |     } else if fn_arg(a) {
-   |               ^^^^^^^^^
-   |
-note: same as this
+error: these `if` branches have the same function call
   --> tests/ui/same_functions_in_if_condition.rs:38:8
    |
 LL |     if fn_arg(a) {
    |        ^^^^^^^^^
+LL |
+LL |     } else if fn_arg(a) {
+   |               ^^^^^^^^^
 
-error: this `if` has the same function call as a previous `if`
-  --> tests/ui/same_functions_in_if_condition.rs:44:15
-   |
-LL |     } else if obj.method() {
-   |               ^^^^^^^^^^^^
-   |
-note: same as this
+error: these `if` branches have the same function call
   --> tests/ui/same_functions_in_if_condition.rs:43:8
    |
 LL |     if obj.method() {
    |        ^^^^^^^^^^^^
+LL |
+LL |     } else if obj.method() {
+   |               ^^^^^^^^^^^^
 
-error: this `if` has the same function call as a previous `if`
-  --> tests/ui/same_functions_in_if_condition.rs:49:15
-   |
-LL |     } else if obj.method_arg(a) {
-   |               ^^^^^^^^^^^^^^^^^
-   |
-note: same as this
+error: these `if` branches have the same function call
   --> tests/ui/same_functions_in_if_condition.rs:48:8
    |
 LL |     if obj.method_arg(a) {
    |        ^^^^^^^^^^^^^^^^^
-
-error: this `if` has the same function call as a previous `if`
-  --> tests/ui/same_functions_in_if_condition.rs:55:15
-   |
-LL |     } else if v.pop().is_none() {
+LL |
+LL |     } else if obj.method_arg(a) {
    |               ^^^^^^^^^^^^^^^^^
-   |
-note: same as this
+
+error: these `if` branches have the same function call
   --> tests/ui/same_functions_in_if_condition.rs:54:8
    |
 LL |     if v.pop().is_none() {
    |        ^^^^^^^^^^^^^^^^^
+LL |
+LL |     } else if v.pop().is_none() {
+   |               ^^^^^^^^^^^^^^^^^
 
-error: this `if` has the same function call as a previous `if`
-  --> tests/ui/same_functions_in_if_condition.rs:60:15
-   |
-LL |     } else if v.len() == 42 {
-   |               ^^^^^^^^^^^^^
-   |
-note: same as this
+error: these `if` branches have the same function call
   --> tests/ui/same_functions_in_if_condition.rs:59:8
    |
 LL |     if v.len() == 42 {
    |        ^^^^^^^^^^^^^
+LL |
+LL |     } else if v.len() == 42 {
+   |               ^^^^^^^^^^^^^
 
 error: aborting due to 6 previous errors