]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | use crate::utils::{is_type_diagnostic_item, match_trait_method, paths, snippet, span_lint_and_sugg}; |
2 | use rustc_errors::Applicability; | |
3 | use rustc_hir as hir; | |
4 | use rustc_lint::LateContext; | |
5 | use rustc_middle::ty; | |
6 | use rustc_span::symbol::sym; | |
7 | ||
8 | use super::MAP_FLATTEN; | |
9 | ||
10 | /// lint use of `map().flatten()` for `Iterators` and 'Options' | |
11 | pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>]) { | |
12 | // lint if caller of `.map().flatten()` is an Iterator | |
13 | if match_trait_method(cx, expr, &paths::ITERATOR) { | |
14 | let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]); | |
15 | let is_map_to_option = match map_closure_ty.kind() { | |
16 | ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => { | |
17 | let map_closure_sig = match map_closure_ty.kind() { | |
18 | ty::Closure(_, substs) => substs.as_closure().sig(), | |
19 | _ => map_closure_ty.fn_sig(cx.tcx), | |
20 | }; | |
21 | let map_closure_return_ty = cx.tcx.erase_late_bound_regions(map_closure_sig.output()); | |
22 | is_type_diagnostic_item(cx, map_closure_return_ty, sym::option_type) | |
23 | }, | |
24 | _ => false, | |
25 | }; | |
26 | ||
27 | let method_to_use = if is_map_to_option { | |
28 | // `(...).map(...)` has type `impl Iterator<Item=Option<...>> | |
29 | "filter_map" | |
30 | } else { | |
31 | // `(...).map(...)` has type `impl Iterator<Item=impl Iterator<...>> | |
32 | "flat_map" | |
33 | }; | |
34 | let func_snippet = snippet(cx, map_args[1].span, ".."); | |
35 | let hint = format!(".{0}({1})", method_to_use, func_snippet); | |
36 | span_lint_and_sugg( | |
37 | cx, | |
38 | MAP_FLATTEN, | |
39 | expr.span.with_lo(map_args[0].span.hi()), | |
40 | "called `map(..).flatten()` on an `Iterator`", | |
41 | &format!("try using `{}` instead", method_to_use), | |
42 | hint, | |
43 | Applicability::MachineApplicable, | |
44 | ); | |
45 | } | |
46 | ||
47 | // lint if caller of `.map().flatten()` is an Option | |
48 | if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type) { | |
49 | let func_snippet = snippet(cx, map_args[1].span, ".."); | |
50 | let hint = format!(".and_then({})", func_snippet); | |
51 | span_lint_and_sugg( | |
52 | cx, | |
53 | MAP_FLATTEN, | |
54 | expr.span.with_lo(map_args[0].span.hi()), | |
55 | "called `map(..).flatten()` on an `Option`", | |
56 | "try using `and_then` instead", | |
57 | hint, | |
58 | Applicability::MachineApplicable, | |
59 | ); | |
60 | } | |
61 | } |