2022-06-19 14:21:14 -05:00
|
|
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
2021-03-25 13:29:11 -05:00
|
|
|
use clippy_utils::is_trait_method;
|
2022-03-24 08:50:04 -05:00
|
|
|
use clippy_utils::source::snippet_with_applicability;
|
2021-03-25 13:29:11 -05:00
|
|
|
use clippy_utils::ty::is_type_diagnostic_item;
|
2021-03-12 08:30:50 -06:00
|
|
|
use rustc_errors::Applicability;
|
2022-03-24 08:50:04 -05:00
|
|
|
use rustc_hir::Expr;
|
2021-03-12 08:30:50 -06:00
|
|
|
use rustc_lint::LateContext;
|
|
|
|
use rustc_middle::ty;
|
2022-03-24 08:50:04 -05:00
|
|
|
use rustc_span::{symbol::sym, Span};
|
2021-03-12 08:30:50 -06:00
|
|
|
|
|
|
|
use super::MAP_FLATTEN;
|
|
|
|
|
|
|
|
/// lint use of `map().flatten()` for `Iterators` and 'Options'
|
2022-03-24 08:50:04 -05:00
|
|
|
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, map_arg: &Expr<'_>, map_span: Span) {
|
|
|
|
if let Some((caller_ty_name, method_to_use)) = try_get_caller_ty_name_and_method_name(cx, expr, recv, map_arg) {
|
|
|
|
let mut applicability = Applicability::MachineApplicable;
|
2022-06-30 03:27:25 -05:00
|
|
|
|
2022-03-24 08:50:04 -05:00
|
|
|
let closure_snippet = snippet_with_applicability(cx, map_arg.span, "..", &mut applicability);
|
2022-06-19 14:21:14 -05:00
|
|
|
span_lint_and_sugg(
|
2021-03-12 08:30:50 -06:00
|
|
|
cx,
|
|
|
|
MAP_FLATTEN,
|
2022-03-24 08:50:04 -05:00
|
|
|
expr.span.with_lo(map_span.lo()),
|
|
|
|
&format!("called `map(..).flatten()` on `{}`", caller_ty_name),
|
2022-06-30 03:27:25 -05:00
|
|
|
&format!(
|
|
|
|
"try replacing `map` with `{}` and remove the `.flatten()`",
|
|
|
|
method_to_use
|
|
|
|
),
|
2022-03-24 08:50:04 -05:00
|
|
|
format!("{}({})", method_to_use, closure_snippet),
|
|
|
|
applicability,
|
2021-03-12 08:30:50 -06:00
|
|
|
);
|
|
|
|
}
|
2022-03-24 08:50:04 -05:00
|
|
|
}
|
2021-03-12 08:30:50 -06:00
|
|
|
|
2022-03-24 08:50:04 -05:00
|
|
|
fn try_get_caller_ty_name_and_method_name(
|
|
|
|
cx: &LateContext<'_>,
|
|
|
|
expr: &Expr<'_>,
|
|
|
|
caller_expr: &Expr<'_>,
|
|
|
|
map_arg: &Expr<'_>,
|
|
|
|
) -> Option<(&'static str, &'static str)> {
|
|
|
|
if is_trait_method(cx, expr, sym::Iterator) {
|
|
|
|
if is_map_to_option(cx, map_arg) {
|
|
|
|
// `(...).map(...)` has type `impl Iterator<Item=Option<...>>
|
|
|
|
Some(("Iterator", "filter_map"))
|
|
|
|
} else {
|
|
|
|
// `(...).map(...)` has type `impl Iterator<Item=impl Iterator<...>>
|
|
|
|
Some(("Iterator", "flat_map"))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(caller_expr).kind() {
|
2022-03-04 14:28:41 -06:00
|
|
|
if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) {
|
2022-03-24 08:50:04 -05:00
|
|
|
return Some(("Option", "and_then"));
|
2022-03-04 14:28:41 -06:00
|
|
|
} else if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) {
|
2022-03-24 08:50:04 -05:00
|
|
|
return Some(("Result", "and_then"));
|
2021-08-12 04:16:25 -05:00
|
|
|
}
|
2022-03-24 08:50:04 -05:00
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2021-08-12 04:16:25 -05:00
|
|
|
|
2022-03-24 08:50:04 -05:00
|
|
|
fn is_map_to_option(cx: &LateContext<'_>, map_arg: &Expr<'_>) -> bool {
|
|
|
|
let map_closure_ty = cx.typeck_results().expr_ty(map_arg);
|
|
|
|
match map_closure_ty.kind() {
|
|
|
|
ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => {
|
|
|
|
let map_closure_sig = match map_closure_ty.kind() {
|
|
|
|
ty::Closure(_, substs) => substs.as_closure().sig(),
|
|
|
|
_ => map_closure_ty.fn_sig(cx.tcx),
|
|
|
|
};
|
|
|
|
let map_closure_return_ty = cx.tcx.erase_late_bound_regions(map_closure_sig.output());
|
|
|
|
is_type_diagnostic_item(cx, map_closure_return_ty, sym::Option)
|
|
|
|
},
|
|
|
|
_ => false,
|
|
|
|
}
|
2021-03-12 08:30:50 -06:00
|
|
|
}
|