]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/loops/manual_flatten.rs
ast/hir: Rename field-related structures
[rust.git] / clippy_lints / src / loops / manual_flatten.rs
1 use super::utils::make_iterator_snippet;
2 use super::MANUAL_FLATTEN;
3 use crate::utils::{is_ok_ctor, is_some_ctor, path_to_local_id, span_lint_and_then};
4 use if_chain::if_chain;
5 use rustc_errors::Applicability;
6 use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, StmtKind};
7 use rustc_lint::LateContext;
8 use rustc_span::source_map::Span;
9
10 /// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the
11 /// iterator element is used.
12 pub(super) fn check<'tcx>(
13     cx: &LateContext<'tcx>,
14     pat: &'tcx Pat<'_>,
15     arg: &'tcx Expr<'_>,
16     body: &'tcx Expr<'_>,
17     span: Span,
18 ) {
19     if let ExprKind::Block(ref block, _) = body.kind {
20         // Ensure the `if let` statement is the only expression or statement in the for-loop
21         let inner_expr = if block.stmts.len() == 1 && block.expr.is_none() {
22             let match_stmt = &block.stmts[0];
23             if let StmtKind::Semi(inner_expr) = match_stmt.kind {
24                 Some(inner_expr)
25             } else {
26                 None
27             }
28         } else if block.stmts.is_empty() {
29             block.expr
30         } else {
31             None
32         };
33
34         if_chain! {
35             if let Some(inner_expr) = inner_expr;
36             if let ExprKind::Match(
37                 ref match_expr, ref match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false }
38             ) = inner_expr.kind;
39             // Ensure match_expr in `if let` statement is the same as the pat from the for-loop
40             if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind;
41             if path_to_local_id(match_expr, pat_hir_id);
42             // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result`
43             if let PatKind::TupleStruct(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind;
44             let some_ctor = is_some_ctor(cx, path.res);
45             let ok_ctor = is_ok_ctor(cx, path.res);
46             if some_ctor || ok_ctor;
47             let if_let_type = if some_ctor { "Some" } else { "Ok" };
48
49             then {
50                 // Prepare the error message
51                 let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type);
52
53                 // Prepare the help message
54                 let mut applicability = Applicability::MaybeIncorrect;
55                 let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability);
56
57                 span_lint_and_then(
58                     cx,
59                     MANUAL_FLATTEN,
60                     span,
61                     &msg,
62                     |diag| {
63                         let sugg = format!("{}.flatten()", arg_snippet);
64                         diag.span_suggestion(
65                             arg.span,
66                             "try",
67                             sugg,
68                             Applicability::MaybeIncorrect,
69                         );
70                         diag.span_help(
71                             inner_expr.span,
72                             "...and remove the `if let` statement in the for loop",
73                         );
74                     }
75                 );
76             }
77         }
78     }
79 }