1 use super::utils::make_iterator_snippet;
2 use super::MANUAL_FLATTEN;
3 use clippy_utils::diagnostics::span_lint_and_then;
4 use clippy_utils::higher;
5 use clippy_utils::visitors::is_local_used;
6 use clippy_utils::{is_lang_ctor, path_to_local_id, peel_blocks_with_stmt};
7 use if_chain::if_chain;
8 use rustc_errors::Applicability;
9 use rustc_hir::LangItem::{OptionSome, ResultOk};
10 use rustc_hir::{Expr, Pat, PatKind};
11 use rustc_lint::LateContext;
13 use rustc_span::source_map::Span;
15 /// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the
16 /// iterator element is used.
17 pub(super) fn check<'tcx>(
18 cx: &LateContext<'tcx>,
24 let inner_expr = peel_blocks_with_stmt(body);
26 if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: None })
27 = higher::IfLet::hir(cx, inner_expr);
28 // Ensure match_expr in `if let` statement is the same as the pat from the for-loop
29 if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind;
30 if path_to_local_id(let_expr, pat_hir_id);
31 // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result`
32 if let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind;
33 let some_ctor = is_lang_ctor(cx, qpath, OptionSome);
34 let ok_ctor = is_lang_ctor(cx, qpath, ResultOk);
35 if some_ctor || ok_ctor;
36 // Ensure expr in `if let` is not used afterwards
37 if !is_local_used(cx, if_then, pat_hir_id);
39 let if_let_type = if some_ctor { "Some" } else { "Ok" };
40 // Prepare the error message
41 let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type);
43 // Prepare the help message
44 let mut applicability = Applicability::MaybeIncorrect;
45 let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability);
46 let copied = match cx.typeck_results().expr_ty(let_expr).kind() {
47 ty::Ref(_, inner, _) => match inner.kind() {
48 ty::Ref(..) => ".copied()",
60 let sugg = format!("{}{}.flatten()", arg_snippet, copied);
65 Applicability::MaybeIncorrect,
69 "...and remove the `if let` statement in the for loop",