]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs
Auto merge of #102318 - Amanieu:default_alloc_error_handler, r=oli-obk
[rust.git] / src / tools / clippy / clippy_lints / src / matches / match_as_ref.rs
1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use clippy_utils::source::snippet_with_applicability;
3 use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks};
4 use rustc_errors::Applicability;
5 use rustc_hir::{Arm, BindingAnnotation, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath};
6 use rustc_lint::LateContext;
7 use rustc_middle::ty;
8
9 use super::MATCH_AS_REF;
10
11 pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
12     if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
13         let arm_ref_mut = if is_none_arm(cx, &arms[0]) {
14             is_ref_some_arm(cx, &arms[1])
15         } else if is_none_arm(cx, &arms[1]) {
16             is_ref_some_arm(cx, &arms[0])
17         } else {
18             None
19         };
20         if let Some(rb) = arm_ref_mut {
21             let suggestion = match rb {
22                 Mutability::Not => "as_ref",
23                 Mutability::Mut => "as_mut",
24             };
25
26             let output_ty = cx.typeck_results().expr_ty(expr);
27             let input_ty = cx.typeck_results().expr_ty(ex);
28
29             let cast = if_chain! {
30                 if let ty::Adt(_, substs) = input_ty.kind();
31                 let input_ty = substs.type_at(0);
32                 if let ty::Adt(_, substs) = output_ty.kind();
33                 let output_ty = substs.type_at(0);
34                 if let ty::Ref(_, output_ty, _) = *output_ty.kind();
35                 if input_ty != output_ty;
36                 then {
37                     ".map(|x| x as _)"
38                 } else {
39                     ""
40                 }
41             };
42
43             let mut applicability = Applicability::MachineApplicable;
44             span_lint_and_sugg(
45                 cx,
46                 MATCH_AS_REF,
47                 expr.span,
48                 &format!("use `{suggestion}()` instead"),
49                 "try this",
50                 format!(
51                     "{}.{suggestion}(){cast}",
52                     snippet_with_applicability(cx, ex.span, "_", &mut applicability),
53                 ),
54                 applicability,
55             );
56         }
57     }
58 }
59
60 // Checks if arm has the form `None => None`
61 fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
62     matches!(
63         arm.pat.kind,
64         PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionNone)
65     )
66 }
67
68 // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
69 fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<Mutability> {
70     if_chain! {
71         if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind;
72         if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionSome);
73         if let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., ident, _) = first_pat.kind;
74         if let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind;
75         if is_res_lang_ctor(cx, path_res(cx, e), LangItem::OptionSome);
76         if let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind;
77         if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
78         then {
79             return Some(mutabl)
80         }
81     }
82     None
83 }