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