]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
Auto merge of #106371 - RalfJung:no-ret-position-noalias, r=nikic
[rust.git] / src / tools / clippy / clippy_lints / src / loops / manual_flatten.rs
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::{path_to_local_id, peel_blocks_with_stmt};
7 use if_chain::if_chain;
8 use rustc_errors::Applicability;
9 use rustc_hir::def::{DefKind, Res};
10 use rustc_hir::{Expr, Pat, PatKind};
11 use rustc_lint::LateContext;
12 use rustc_middle::ty::{self, DefIdTree};
13 use rustc_span::source_map::Span;
14
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>,
19     pat: &'tcx Pat<'_>,
20     arg: &'tcx Expr<'_>,
21     body: &'tcx Expr<'_>,
22     span: Span,
23 ) {
24     let inner_expr = peel_blocks_with_stmt(body);
25     if_chain! {
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         if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, let_pat.hir_id);
34         if let Some(variant_id) = cx.tcx.opt_parent(ctor_id);
35         let some_ctor = cx.tcx.lang_items().option_some_variant() == Some(variant_id);
36         let ok_ctor = cx.tcx.lang_items().result_ok_variant() == Some(variant_id);
37         if some_ctor || ok_ctor;
38         // Ensure expr in `if let` is not used afterwards
39         if !is_local_used(cx, if_then, pat_hir_id);
40         then {
41             let if_let_type = if some_ctor { "Some" } else { "Ok" };
42             // Prepare the error message
43             let msg = format!("unnecessary `if let` since only the `{if_let_type}` variant of the iterator element is used");
44
45             // Prepare the help message
46             let mut applicability = Applicability::MaybeIncorrect;
47             let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability);
48             let copied = match cx.typeck_results().expr_ty(let_expr).kind() {
49                 ty::Ref(_, inner, _) => match inner.kind() {
50                     ty::Ref(..) => ".copied()",
51                     _ => ""
52                 }
53                 _ => ""
54             };
55
56             let sugg = format!("{arg_snippet}{copied}.flatten()");
57
58             // If suggestion is not a one-liner, it won't be shown inline within the error message. In that case,
59             // it will be shown in the extra `help` message at the end, which is why the first `help_msg` needs
60             // to refer to the correct relative position of the suggestion.
61             let help_msg = if sugg.contains('\n') {
62                 "remove the `if let` statement in the for loop and then..."
63             } else {
64                 "...and remove the `if let` statement in the for loop"
65             };
66
67             span_lint_and_then(
68                 cx,
69                 MANUAL_FLATTEN,
70                 span,
71                 &msg,
72                 |diag| {
73                     diag.span_suggestion(
74                         arg.span,
75                         "try",
76                         sugg,
77                         applicability,
78                     );
79                     diag.span_help(
80                         inner_expr.span,
81                         help_msg,
82                     );
83                 }
84             );
85         }
86     }
87 }