|
11 | 11 | use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
12 | 12 | use crate::rustc::{declare_tool_lint, lint_array};
|
13 | 13 | use crate::rustc::hir::*;
|
| 14 | +use crate::syntax::ptr::P; |
| 15 | +use crate::syntax::ast::LitKind; |
14 | 16 | use crate::utils::{match_qpath, paths, snippet, span_lint_and_then};
|
15 | 17 | use crate::rustc_errors::Applicability;
|
16 | 18 |
|
@@ -58,46 +60,164 @@ impl LintPass for Pass {
|
58 | 60 | impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
59 | 61 | #[allow(clippy::similar_names)]
|
60 | 62 | fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
61 |
| - if let ExprKind::Match(ref op, ref arms, MatchSource::IfLetDesugar { .. }) = expr.node { |
62 |
| - if arms[0].pats.len() == 1 { |
63 |
| - let good_method = match arms[0].pats[0].node { |
64 |
| - PatKind::TupleStruct(ref path, ref pats, _) if pats.len() == 1 => { |
65 |
| - if let PatKind::Wild = pats[0].node { |
66 |
| - if match_qpath(path, &paths::RESULT_OK) { |
67 |
| - "is_ok()" |
68 |
| - } else if match_qpath(path, &paths::RESULT_ERR) { |
69 |
| - "is_err()" |
70 |
| - } else if match_qpath(path, &paths::OPTION_SOME) { |
71 |
| - "is_some()" |
72 |
| - } else { |
73 |
| - return; |
74 |
| - } |
75 |
| - } else { |
76 |
| - return; |
77 |
| - } |
78 |
| - }, |
| 63 | + if let ExprKind::Match(ref op, ref arms, ref match_source) = expr.node { |
| 64 | + match match_source { |
| 65 | + MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms), |
| 66 | + MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms), |
| 67 | + _ => return, |
| 68 | + } |
| 69 | + } |
| 70 | + } |
| 71 | +} |
79 | 72 |
|
80 |
| - PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => "is_none()", |
| 73 | +fn find_sugg_for_if_let<'a, 'tcx>( |
| 74 | + cx: &LateContext<'a, 'tcx>, |
| 75 | + expr: &'tcx Expr, |
| 76 | + op: &P<Expr>, |
| 77 | + arms: &HirVec<Arm> |
| 78 | +) { |
| 79 | + if arms[0].pats.len() == 1 { |
| 80 | + let good_method = match arms[0].pats[0].node { |
| 81 | + PatKind::TupleStruct(ref path, ref pats, _) if pats.len() == 1 => { |
| 82 | + if let PatKind::Wild = pats[0].node { |
| 83 | + if match_qpath(path, &paths::RESULT_OK) { |
| 84 | + "is_ok()" |
| 85 | + } else if match_qpath(path, &paths::RESULT_ERR) { |
| 86 | + "is_err()" |
| 87 | + } else if match_qpath(path, &paths::OPTION_SOME) { |
| 88 | + "is_some()" |
| 89 | + } else { |
| 90 | + return; |
| 91 | + } |
| 92 | + } else { |
| 93 | + return; |
| 94 | + } |
| 95 | + }, |
81 | 96 |
|
82 |
| - _ => return, |
83 |
| - }; |
| 97 | + PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => "is_none()", |
84 | 98 |
|
85 |
| - span_lint_and_then( |
86 |
| - cx, |
87 |
| - IF_LET_REDUNDANT_PATTERN_MATCHING, |
88 |
| - arms[0].pats[0].span, |
89 |
| - &format!("redundant pattern matching, consider using `{}`", good_method), |
90 |
| - |db| { |
91 |
| - let span = expr.span.to(op.span); |
92 |
| - db.span_suggestion_with_applicability( |
93 |
| - span, |
94 |
| - "try this", |
95 |
| - format!("if {}.{}", snippet(cx, op.span, "_"), good_method), |
96 |
| - Applicability::MachineApplicable, // snippet |
97 |
| - ); |
98 |
| - }, |
| 99 | + _ => return, |
| 100 | + }; |
| 101 | + |
| 102 | + span_lint_and_then( |
| 103 | + cx, |
| 104 | + IF_LET_REDUNDANT_PATTERN_MATCHING, |
| 105 | + arms[0].pats[0].span, |
| 106 | + &format!("redundant pattern matching, consider using `{}`", good_method), |
| 107 | + |db| { |
| 108 | + let span = expr.span.to(op.span); |
| 109 | + db.span_suggestion_with_applicability( |
| 110 | + span, |
| 111 | + "try this", |
| 112 | + format!("if {}.{}", snippet(cx, op.span, "_"), good_method), |
| 113 | + Applicability::MachineApplicable, // snippet |
99 | 114 | );
|
100 |
| - } |
| 115 | + }, |
| 116 | + ); |
| 117 | + } else { |
| 118 | + return; |
| 119 | + } |
| 120 | +} |
| 121 | + |
| 122 | +fn find_sugg_for_match<'a, 'tcx>( |
| 123 | + cx: &LateContext<'a, 'tcx>, |
| 124 | + expr: &'tcx Expr, |
| 125 | + op: &P<Expr>, |
| 126 | + arms: &HirVec<Arm> |
| 127 | +) { |
| 128 | + if arms.len() == 2 { |
| 129 | + let node_pair = (&arms[0].pats[0].node, &arms[1].pats[0].node); |
| 130 | + |
| 131 | + let found_good_method = match node_pair { |
| 132 | + ( |
| 133 | + PatKind::TupleStruct(ref path_left, ref pats_left, _), |
| 134 | + PatKind::TupleStruct(ref path_right, ref pats_right, _) |
| 135 | + ) if pats_left.len() == 1 && pats_right.len() == 1 => { |
| 136 | + if let (PatKind::Wild, PatKind::Wild) = (&pats_left[0].node, &pats_right[0].node) { |
| 137 | + find_good_method_for_match( |
| 138 | + arms, |
| 139 | + path_left, |
| 140 | + path_right, |
| 141 | + &paths::RESULT_OK, |
| 142 | + &paths::RESULT_ERR, |
| 143 | + "is_ok()", |
| 144 | + "is_err()" |
| 145 | + ) |
| 146 | + } else { |
| 147 | + None |
| 148 | + } |
| 149 | + }, |
| 150 | + ( |
| 151 | + PatKind::TupleStruct(ref path_left, ref pats, _), |
| 152 | + PatKind::Path(ref path_right) |
| 153 | + ) | ( |
| 154 | + PatKind::Path(ref path_left), |
| 155 | + PatKind::TupleStruct(ref path_right, ref pats, _) |
| 156 | + ) if pats.len() == 1 => { |
| 157 | + if let PatKind::Wild = pats[0].node { |
| 158 | + find_good_method_for_match( |
| 159 | + arms, |
| 160 | + path_left, |
| 161 | + path_right, |
| 162 | + &paths::OPTION_SOME, |
| 163 | + &paths::OPTION_NONE, |
| 164 | + "is_some()", |
| 165 | + "is_none()" |
| 166 | + ) |
| 167 | + } else { |
| 168 | + None |
| 169 | + } |
| 170 | + }, |
| 171 | + _ => None, |
| 172 | + }; |
| 173 | + |
| 174 | + if let Some(good_method) = found_good_method { |
| 175 | + span_lint_and_then( |
| 176 | + cx, |
| 177 | + IF_LET_REDUNDANT_PATTERN_MATCHING, |
| 178 | + expr.span, |
| 179 | + &format!("redundant pattern matching, consider using `{}`", good_method), |
| 180 | + |db| { |
| 181 | + let span = expr.span.to(op.span); |
| 182 | + db.span_suggestion_with_applicability( |
| 183 | + span, |
| 184 | + "try this", |
| 185 | + format!("{}.{}", snippet(cx, op.span, "_"), good_method), |
| 186 | + Applicability::MachineApplicable, // snippet |
| 187 | + ); |
| 188 | + }, |
| 189 | + ); |
101 | 190 | }
|
| 191 | + } else { |
| 192 | + return; |
| 193 | + } |
| 194 | +} |
| 195 | + |
| 196 | +fn find_good_method_for_match<'a>( |
| 197 | + arms: &HirVec<Arm>, |
| 198 | + path_left: &QPath, |
| 199 | + path_right: &QPath, |
| 200 | + expected_left: &[&str], |
| 201 | + expected_right: &[&str], |
| 202 | + should_be_left: &'a str, |
| 203 | + should_be_right: &'a str |
| 204 | +) -> Option<&'a str> { |
| 205 | + let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) { |
| 206 | + (&(*arms[0].body).node, &(*arms[1].body).node) |
| 207 | + } else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) { |
| 208 | + (&(*arms[1].body).node, &(*arms[0].body).node) |
| 209 | + } else { |
| 210 | + return None; |
| 211 | + }; |
| 212 | + |
| 213 | + match body_node_pair { |
| 214 | + (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => { |
| 215 | + match (&lit_left.node, &lit_right.node) { |
| 216 | + (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left), |
| 217 | + (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right), |
| 218 | + _ => None, |
| 219 | + } |
| 220 | + }, |
| 221 | + _ => None, |
102 | 222 | }
|
103 | 223 | }
|
0 commit comments