about summary refs log tree commit diff
path: root/clippy_lints/src/unnecessary_map_on_constructor.rs
blob: 8f1eb5019f0fcf386ac4c24940441082320d2719 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::get_type_diagnostic_name;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::sym;

declare_clippy_lint! {
    /// ### What it does
    /// Suggests removing the use of a `map()` (or `map_err()`) method when an `Option` or `Result`
    /// is being constructed.
    ///
    /// ### Why is this bad?
    /// It introduces unnecessary complexity. Instead, the function can be called before
    /// constructing the `Option` or `Result` from its return value.
    ///
    /// ### Example
    /// ```no_run
    /// Some(4).map(i32::swap_bytes)
    /// # ;
    /// ```
    /// Use instead:
    /// ```no_run
    /// Some(i32::swap_bytes(4))
    /// # ;
    /// ```
    #[clippy::version = "1.74.0"]
    pub UNNECESSARY_MAP_ON_CONSTRUCTOR,
    complexity,
    "using `map`/`map_err` on `Option` or `Result` constructors"
}
declare_lint_pass!(UnnecessaryMapOnConstructor => [UNNECESSARY_MAP_ON_CONSTRUCTOR]);

impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
        if expr.span.from_expansion() {
            return;
        }
        if let hir::ExprKind::MethodCall(path, recv, args, ..) = expr.kind
            && let Some(sym::Option | sym::Result) = get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv))
        {
            let (constructor_path, constructor_item) = if let hir::ExprKind::Call(constructor, constructor_args) =
                recv.kind
                && let hir::ExprKind::Path(constructor_path) = constructor.kind
                && let Some(arg) = constructor_args.first()
            {
                if constructor.span.from_expansion() || arg.span.from_expansion() {
                    return;
                }
                (constructor_path, arg)
            } else {
                return;
            };
            let constructor_symbol = match constructor_path {
                hir::QPath::Resolved(_, path) => {
                    if let Some(path_segment) = path.segments.last() {
                        path_segment.ident.name
                    } else {
                        return;
                    }
                },
                hir::QPath::TypeRelative(_, path) => path.ident.name,
                hir::QPath::LangItem(..) => return,
            };
            match constructor_symbol {
                sym::Some | sym::Ok if path.ident.name == sym::map => (),
                sym::Err if path.ident.name == sym::map_err => (),
                _ => return,
            }

            if let Some(map_arg) = args.first()
                && let hir::ExprKind::Path(fun) = map_arg.kind
            {
                if map_arg.span.from_expansion() {
                    return;
                }
                let mut applicability = Applicability::MachineApplicable;
                let fun_snippet = snippet_with_applicability(cx, fun.span(), "_", &mut applicability);
                let constructor_snippet =
                    snippet_with_applicability(cx, constructor_path.span(), "_", &mut applicability);
                let constructor_arg_snippet =
                    snippet_with_applicability(cx, constructor_item.span, "_", &mut applicability);
                span_lint_and_sugg(
                    cx,
                    UNNECESSARY_MAP_ON_CONSTRUCTOR,
                    expr.span,
                    format!(
                        "unnecessary {} on constructor {constructor_snippet}(_)",
                        path.ident.name
                    ),
                    "try",
                    format!("{constructor_snippet}({fun_snippet}({constructor_arg_snippet}))"),
                    applicability,
                );
            }
        }
    }
}