]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/methods/map_flatten.rs
Split out `infalliable_detructuring_match`
[rust.git] / clippy_lints / src / methods / map_flatten.rs
1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use clippy_utils::is_trait_method;
3 use clippy_utils::source::snippet;
4 use clippy_utils::ty::is_type_diagnostic_item;
5 use rustc_errors::Applicability;
6 use rustc_hir as hir;
7 use rustc_lint::LateContext;
8 use rustc_middle::ty;
9 use rustc_span::symbol::sym;
10
11 use super::MAP_FLATTEN;
12
13 /// lint use of `map().flatten()` for `Iterators` and 'Options'
14 pub(super) fn check<'tcx>(
15     cx: &LateContext<'tcx>,
16     expr: &'tcx hir::Expr<'_>,
17     recv: &'tcx hir::Expr<'_>,
18     map_arg: &'tcx hir::Expr<'_>,
19 ) {
20     // lint if caller of `.map().flatten()` is an Iterator
21     if is_trait_method(cx, expr, sym::Iterator) {
22         let map_closure_ty = cx.typeck_results().expr_ty(map_arg);
23         let is_map_to_option = match map_closure_ty.kind() {
24             ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => {
25                 let map_closure_sig = match map_closure_ty.kind() {
26                     ty::Closure(_, substs) => substs.as_closure().sig(),
27                     _ => map_closure_ty.fn_sig(cx.tcx),
28                 };
29                 let map_closure_return_ty = cx.tcx.erase_late_bound_regions(map_closure_sig.output());
30                 is_type_diagnostic_item(cx, map_closure_return_ty, sym::Option)
31             },
32             _ => false,
33         };
34
35         let method_to_use = if is_map_to_option {
36             // `(...).map(...)` has type `impl Iterator<Item=Option<...>>
37             "filter_map"
38         } else {
39             // `(...).map(...)` has type `impl Iterator<Item=impl Iterator<...>>
40             "flat_map"
41         };
42         let func_snippet = snippet(cx, map_arg.span, "..");
43         let hint = format!(".{0}({1})", method_to_use, func_snippet);
44         span_lint_and_sugg(
45             cx,
46             MAP_FLATTEN,
47             expr.span.with_lo(recv.span.hi()),
48             "called `map(..).flatten()` on an `Iterator`",
49             &format!("try using `{}` instead", method_to_use),
50             hint,
51             Applicability::MachineApplicable,
52         );
53     }
54
55     // lint if caller of `.map().flatten()` is an Option or Result
56     let caller_type = match cx.typeck_results().expr_ty(recv).kind() {
57         ty::Adt(adt, _) => {
58             if cx.tcx.is_diagnostic_item(sym::Option, adt.did) {
59                 "Option"
60             } else if cx.tcx.is_diagnostic_item(sym::Result, adt.did) {
61                 "Result"
62             } else {
63                 return;
64             }
65         },
66         _ => {
67             return;
68         },
69     };
70
71     let func_snippet = snippet(cx, map_arg.span, "..");
72     let hint = format!(".and_then({})", func_snippet);
73     let lint_info = format!("called `map(..).flatten()` on an `{}`", caller_type);
74     span_lint_and_sugg(
75         cx,
76         MAP_FLATTEN,
77         expr.span.with_lo(recv.span.hi()),
78         &lint_info,
79         "try using `and_then` instead",
80         hint,
81         Applicability::MachineApplicable,
82     );
83 }