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;
7 use rustc_lint::LateContext;
9 use rustc_span::symbol::sym;
11 use super::MAP_FLATTEN;
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<'_>,
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),
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_type)
35 let method_to_use = if is_map_to_option {
36 // `(...).map(...)` has type `impl Iterator<Item=Option<...>>
39 // `(...).map(...)` has type `impl Iterator<Item=impl Iterator<...>>
42 let func_snippet = snippet(cx, map_arg.span, "..");
43 let hint = format!(".{0}({1})", method_to_use, func_snippet);
47 expr.span.with_lo(recv.span.hi()),
48 "called `map(..).flatten()` on an `Iterator`",
49 &format!("try using `{}` instead", method_to_use),
51 Applicability::MachineApplicable,
55 // lint if caller of `.map().flatten()` is an Option
56 if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type) {
57 let func_snippet = snippet(cx, map_arg.span, "..");
58 let hint = format!(".and_then({})", func_snippet);
62 expr.span.with_lo(recv.span.hi()),
63 "called `map(..).flatten()` on an `Option`",
64 "try using `and_then` instead",
66 Applicability::MachineApplicable,