]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
New upstream version 1.70.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / loops / manual_flatten.rs
CommitLineData
f20569fa
XL
1use super::utils::make_iterator_snippet;
2use super::MANUAL_FLATTEN;
cdc7bbd5 3use clippy_utils::diagnostics::span_lint_and_then;
94222f64 4use clippy_utils::higher;
c295e0f8 5use clippy_utils::visitors::is_local_used;
2b03887a 6use clippy_utils::{path_to_local_id, peel_blocks_with_stmt};
f20569fa
XL
7use if_chain::if_chain;
8use rustc_errors::Applicability;
2b03887a 9use rustc_hir::def::{DefKind, Res};
a2a8927a 10use rustc_hir::{Expr, Pat, PatKind};
f20569fa 11use rustc_lint::LateContext;
353b0b11 12use rustc_middle::ty;
f20569fa
XL
13use 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.
17pub(super) fn check<'tcx>(
18 cx: &LateContext<'tcx>,
19 pat: &'tcx Pat<'_>,
20 arg: &'tcx Expr<'_>,
21 body: &'tcx Expr<'_>,
22 span: Span,
23) {
a2a8927a
XL
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;
2b03887a
FG
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);
a2a8927a
XL
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
2b03887a 43 let msg = format!("unnecessary `if let` since only the `{if_let_type}` variant of the iterator element is used");
f20569fa 44
a2a8927a
XL
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()",
cdc7bbd5 51 _ => ""
a2a8927a
XL
52 }
53 _ => ""
54 };
f20569fa 55
064997fb
FG
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
a2a8927a
XL
67 span_lint_and_then(
68 cx,
69 MANUAL_FLATTEN,
70 span,
71 &msg,
72 |diag| {
a2a8927a
XL
73 diag.span_suggestion(
74 arg.span,
75 "try",
76 sugg,
064997fb 77 applicability,
a2a8927a
XL
78 );
79 diag.span_help(
80 inner_expr.span,
064997fb 81 help_msg,
a2a8927a
XL
82 );
83 }
84 );
f20569fa
XL
85 }
86 }
87}