]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/matches/manual_map.rs
Auto merge of #105592 - matthiaskrgr:rollup-1cazogq, r=matthiaskrgr
[rust.git] / src / tools / clippy / clippy_lints / src / matches / manual_map.rs
1 use super::manual_utils::{check_with, SomeExpr};
2 use super::MANUAL_MAP;
3 use clippy_utils::diagnostics::span_lint_and_sugg;
4
5 use clippy_utils::{is_res_lang_ctor, path_res};
6
7 use rustc_hir::LangItem::OptionSome;
8 use rustc_hir::{Arm, Block, BlockCheckMode, Expr, ExprKind, Pat, UnsafeSource};
9 use rustc_lint::LateContext;
10 use rustc_span::SyntaxContext;
11
12 pub(super) fn check_match<'tcx>(
13     cx: &LateContext<'tcx>,
14     expr: &'tcx Expr<'_>,
15     scrutinee: &'tcx Expr<'_>,
16     arms: &'tcx [Arm<'_>],
17 ) {
18     if let [arm1, arm2] = arms
19         && arm1.guard.is_none()
20         && arm2.guard.is_none()
21     {
22         check(cx, expr, scrutinee, arm1.pat, arm1.body, Some(arm2.pat), arm2.body);
23     }
24 }
25
26 pub(super) fn check_if_let<'tcx>(
27     cx: &LateContext<'tcx>,
28     expr: &'tcx Expr<'_>,
29     let_pat: &'tcx Pat<'_>,
30     let_expr: &'tcx Expr<'_>,
31     then_expr: &'tcx Expr<'_>,
32     else_expr: &'tcx Expr<'_>,
33 ) {
34     check(cx, expr, let_expr, let_pat, then_expr, None, else_expr);
35 }
36
37 fn check<'tcx>(
38     cx: &LateContext<'tcx>,
39     expr: &'tcx Expr<'_>,
40     scrutinee: &'tcx Expr<'_>,
41     then_pat: &'tcx Pat<'_>,
42     then_body: &'tcx Expr<'_>,
43     else_pat: Option<&'tcx Pat<'_>>,
44     else_body: &'tcx Expr<'_>,
45 ) {
46     if let Some(sugg_info) = check_with(
47         cx,
48         expr,
49         scrutinee,
50         then_pat,
51         then_body,
52         else_pat,
53         else_body,
54         get_some_expr,
55     ) {
56         span_lint_and_sugg(
57             cx,
58             MANUAL_MAP,
59             expr.span,
60             "manual implementation of `Option::map`",
61             "try this",
62             if sugg_info.needs_brackets {
63                 format!(
64                     "{{ {}{}.map({}) }}",
65                     sugg_info.scrutinee_str, sugg_info.as_ref_str, sugg_info.body_str
66                 )
67             } else {
68                 format!(
69                     "{}{}.map({})",
70                     sugg_info.scrutinee_str, sugg_info.as_ref_str, sugg_info.body_str
71                 )
72             },
73             sugg_info.app,
74         );
75     }
76 }
77
78 // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression.
79 fn get_some_expr<'tcx>(
80     cx: &LateContext<'tcx>,
81     _: &'tcx Pat<'_>,
82     expr: &'tcx Expr<'_>,
83     ctxt: SyntaxContext,
84 ) -> Option<SomeExpr<'tcx>> {
85     fn get_some_expr_internal<'tcx>(
86         cx: &LateContext<'tcx>,
87         expr: &'tcx Expr<'_>,
88         needs_unsafe_block: bool,
89         ctxt: SyntaxContext,
90     ) -> Option<SomeExpr<'tcx>> {
91         // TODO: Allow more complex expressions.
92         match expr.kind {
93             ExprKind::Call(callee, [arg])
94                 if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) =>
95             {
96                 Some(SomeExpr::new_no_negated(arg, needs_unsafe_block))
97             },
98             ExprKind::Block(
99                 Block {
100                     stmts: [],
101                     expr: Some(expr),
102                     rules,
103                     ..
104                 },
105                 _,
106             ) => get_some_expr_internal(
107                 cx,
108                 expr,
109                 needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
110                 ctxt,
111             ),
112             _ => None,
113         }
114     }
115     get_some_expr_internal(cx, expr, false, ctxt)
116 }