]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/redundant_pattern_matching.rs
Merge remote-tracking branch 'origin/rust-1.31.0' into HEAD
[rust.git] / clippy_lints / src / redundant_pattern_matching.rs
1 use crate::utils::{match_qpath, paths, snippet, span_lint_and_then};
2 use rustc::hir::*;
3 use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
4 use rustc::{declare_lint_pass, declare_tool_lint};
5 use rustc_errors::Applicability;
6 use syntax::ast::LitKind;
7 use syntax::ptr::P;
8 use syntax::symbol::Symbol;
9
10 declare_clippy_lint! {
11     /// **What it does:** Lint for redundant pattern matching over `Result` or
12     /// `Option`
13     ///
14     /// **Why is this bad?** It's more concise and clear to just use the proper
15     /// utility function
16     ///
17     /// **Known problems:** None.
18     ///
19     /// **Example:**
20     ///
21     /// ```rust
22     /// if let Ok(_) = Ok::<i32, i32>(42) {}
23     /// if let Err(_) = Err::<i32, i32>(42) {}
24     /// if let None = None::<()> {}
25     /// if let Some(_) = Some(42) {}
26     /// match Ok::<i32, i32>(42) {
27     ///     Ok(_) => true,
28     ///     Err(_) => false,
29     /// };
30     /// ```
31     ///
32     /// The more idiomatic use would be:
33     ///
34     /// ```rust
35     /// if Ok::<i32, i32>(42).is_ok() {}
36     /// if Err::<i32, i32>(42).is_err() {}
37     /// if None::<()>.is_none() {}
38     /// if Some(42).is_some() {}
39     /// Ok::<i32, i32>(42).is_ok();
40     /// ```
41     pub REDUNDANT_PATTERN_MATCHING,
42     style,
43     "use the proper utility function avoiding an `if let`"
44 }
45
46 declare_lint_pass!(RedundantPatternMatching => [REDUNDANT_PATTERN_MATCHING]);
47
48 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantPatternMatching {
49     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
50         if let ExprKind::Match(ref op, ref arms, ref match_source) = expr.node {
51             match match_source {
52                 MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms),
53                 MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms),
54                 _ => return,
55             }
56         }
57     }
58 }
59
60 fn find_sugg_for_if_let<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr, op: &P<Expr>, arms: &HirVec<Arm>) {
61     if arms[0].pats.len() == 1 {
62         let good_method = match arms[0].pats[0].node {
63             PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => {
64                 if let PatKind::Wild = patterns[0].node {
65                     if match_qpath(path, &*paths::RESULT_OK) {
66                         "is_ok()"
67                     } else if match_qpath(path, &*paths::RESULT_ERR) {
68                         "is_err()"
69                     } else if match_qpath(path, &*paths::OPTION_SOME) {
70                         "is_some()"
71                     } else {
72                         return;
73                     }
74                 } else {
75                     return;
76                 }
77             },
78
79             PatKind::Path(ref path) if match_qpath(path, &*paths::OPTION_NONE) => "is_none()",
80
81             _ => return,
82         };
83
84         span_lint_and_then(
85             cx,
86             REDUNDANT_PATTERN_MATCHING,
87             arms[0].pats[0].span,
88             &format!("redundant pattern matching, consider using `{}`", good_method),
89             |db| {
90                 let span = expr.span.to(op.span);
91                 db.span_suggestion(
92                     span,
93                     "try this",
94                     format!("if {}.{}", snippet(cx, op.span, "_"), good_method),
95                     Applicability::MachineApplicable, // snippet
96                 );
97             },
98         );
99     } else {
100         return;
101     }
102 }
103
104 fn find_sugg_for_match<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr, op: &P<Expr>, arms: &HirVec<Arm>) {
105     if arms.len() == 2 {
106         let node_pair = (&arms[0].pats[0].node, &arms[1].pats[0].node);
107
108         let found_good_method = match node_pair {
109             (
110                 PatKind::TupleStruct(ref path_left, ref patterns_left, _),
111                 PatKind::TupleStruct(ref path_right, ref patterns_right, _),
112             ) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
113                 if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].node, &patterns_right[0].node) {
114                     find_good_method_for_match(
115                         arms,
116                         path_left,
117                         path_right,
118                         &*paths::RESULT_OK,
119                         &*paths::RESULT_ERR,
120                         "is_ok()",
121                         "is_err()",
122                     )
123                 } else {
124                     None
125                 }
126             },
127             (PatKind::TupleStruct(ref path_left, ref patterns, _), PatKind::Path(ref path_right))
128             | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, ref patterns, _))
129                 if patterns.len() == 1 =>
130             {
131                 if let PatKind::Wild = patterns[0].node {
132                     find_good_method_for_match(
133                         arms,
134                         path_left,
135                         path_right,
136                         &*paths::OPTION_SOME,
137                         &*paths::OPTION_NONE,
138                         "is_some()",
139                         "is_none()",
140                     )
141                 } else {
142                     None
143                 }
144             },
145             _ => None,
146         };
147
148         if let Some(good_method) = found_good_method {
149             span_lint_and_then(
150                 cx,
151                 REDUNDANT_PATTERN_MATCHING,
152                 expr.span,
153                 &format!("redundant pattern matching, consider using `{}`", good_method),
154                 |db| {
155                     let span = expr.span.to(op.span);
156                     db.span_suggestion(
157                         span,
158                         "try this",
159                         format!("{}.{}", snippet(cx, op.span, "_"), good_method),
160                         Applicability::MachineApplicable, // snippet
161                     );
162                 },
163             );
164         }
165     } else {
166         return;
167     }
168 }
169
170 fn find_good_method_for_match<'a>(
171     arms: &HirVec<Arm>,
172     path_left: &QPath,
173     path_right: &QPath,
174     expected_left: &[Symbol],
175     expected_right: &[Symbol],
176     should_be_left: &'a str,
177     should_be_right: &'a str,
178 ) -> Option<&'a str> {
179     let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) {
180         (&(*arms[0].body).node, &(*arms[1].body).node)
181     } else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) {
182         (&(*arms[1].body).node, &(*arms[0].body).node)
183     } else {
184         return None;
185     };
186
187     match body_node_pair {
188         (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
189             (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left),
190             (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right),
191             _ => None,
192         },
193         _ => None,
194     }
195 }