Skip to content

Commit 8630376

Browse files
authored
Merge pull request #1305 from d-dorazio/1289-lint-for-ignored-argument-in-result-option
Add lint for redundant pattern matching in if let for Result/Option
2 parents 502416f + d213040 commit 8630376

13 files changed

+340
-187
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ All notable changes to this project will be documented in this file.
257257
[`for_loop_over_option`]: https://github.com/Manishearth/rust-clippy/wiki#for_loop_over_option
258258
[`for_loop_over_result`]: https://github.com/Manishearth/rust-clippy/wiki#for_loop_over_result
259259
[`identity_op`]: https://github.com/Manishearth/rust-clippy/wiki#identity_op
260+
[`if_let_redundant_pattern_matching`]: https://github.com/Manishearth/rust-clippy/wiki#if_let_redundant_pattern_matching
260261
[`if_let_some_result`]: https://github.com/Manishearth/rust-clippy/wiki#if_let_some_result
261262
[`if_not_else`]: https://github.com/Manishearth/rust-clippy/wiki#if_not_else
262263
[`if_same_then_else`]: https://github.com/Manishearth/rust-clippy/wiki#if_same_then_else

README.md

Lines changed: 179 additions & 178 deletions
Large diffs are not rendered by default.

clippy_lints/src/approx_const.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ fn check_lit(cx: &LateContext, lit: &Lit, e: &Expr) {
7676
}
7777

7878
fn check_known_consts(cx: &LateContext, e: &Expr, s: &str, module: &str) {
79-
if let Ok(_) = s.parse::<f64>() {
79+
if s.parse::<f64>().is_ok() {
8080
for &(constant, name, min_digits) in KNOWN_CONSTS {
8181
if is_approx_const(constant, s, min_digits) {
8282
span_lint(cx,

clippy_lints/src/arithmetic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ impl LintPass for Arithmetic {
4949

5050
impl LateLintPass for Arithmetic {
5151
fn check_expr(&mut self, cx: &LateContext, expr: &hir::Expr) {
52-
if let Some(_) = self.span {
52+
if self.span.is_some() {
5353
return;
5454
}
5555
match expr.node {

clippy_lints/src/doc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ fn check_doc(cx: &EarlyContext, valid_idents: &[String], docs: &[(&str, Span)])
322322

323323
if new_line {
324324
let mut lookup_parser = parser.clone();
325-
if let Some(_) = lookup_parser.find(|&(_, c)| c == ']') {
325+
if lookup_parser.any(|(_, c)| c == ']') {
326326
if let Some((_, ':')) = lookup_parser.next() {
327327
lookup_parser.next_line();
328328
parser = lookup_parser;
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use rustc::lint::*;
2+
use rustc::hir::*;
3+
use syntax::codemap::Span;
4+
use utils::{paths, span_lint_and_then, match_path, snippet};
5+
6+
/// **What it does:*** Lint for redundant pattern matching over `Result` or `Option`
7+
///
8+
/// **Why is this bad?** It's more concise and clear to just use the proper utility function
9+
///
10+
/// **Known problems:** None.
11+
///
12+
/// **Example:**
13+
///
14+
/// ```rust
15+
/// if let Ok(_) = Ok::<i32, i32>(42) {}
16+
/// if let Err(_) = Err::<i32, i32>(42) {}
17+
/// if let None = None::<()> {}
18+
/// if let Some(_) = Some(42) {}
19+
/// ```
20+
///
21+
/// The more idiomatic use would be:
22+
///
23+
/// ```rust
24+
/// if Ok::<i32, i32>(42).is_ok() {}
25+
/// if Err::<i32, i32>(42).is_err() {}
26+
/// if None::<()>.is_none() {}
27+
/// if Some(42).is_some() {}
28+
/// ```
29+
///
30+
declare_lint! {
31+
pub IF_LET_REDUNDANT_PATTERN_MATCHING,
32+
Warn,
33+
"use the proper utility function avoiding an `if let`"
34+
}
35+
36+
#[derive(Copy, Clone)]
37+
pub struct Pass;
38+
39+
impl LintPass for Pass {
40+
fn get_lints(&self) -> LintArray {
41+
lint_array!(IF_LET_REDUNDANT_PATTERN_MATCHING)
42+
}
43+
}
44+
45+
impl LateLintPass for Pass {
46+
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
47+
48+
if let ExprMatch(ref op, ref arms, MatchSource::IfLetDesugar{..}) = expr.node {
49+
50+
if arms[0].pats.len() == 1 {
51+
52+
let good_method = match arms[0].pats[0].node {
53+
PatKind::TupleStruct(ref path, ref pats, _) if pats.len() == 1 && pats[0].node == PatKind::Wild => {
54+
55+
if match_path(path, &paths::RESULT_OK) {
56+
"is_ok()"
57+
} else if match_path(path, &paths::RESULT_ERR) {
58+
"is_err()"
59+
} else if match_path(path, &paths::OPTION_SOME) {
60+
"is_some()"
61+
} else {
62+
return
63+
}
64+
}
65+
66+
PatKind::Path(_, ref path) if match_path(path, &paths::OPTION_NONE) => {
67+
"is_none()"
68+
}
69+
70+
_ => return
71+
};
72+
73+
span_lint_and_then(cx,
74+
IF_LET_REDUNDANT_PATTERN_MATCHING,
75+
arms[0].pats[0].span,
76+
&format!("redundant pattern matching, consider using `{}`", good_method),
77+
|db| {
78+
let span = Span {
79+
lo: expr.span.lo,
80+
hi: op.span.hi,
81+
expn_id: expr.span.expn_id,
82+
};
83+
db.span_suggestion(span,
84+
"try this",
85+
format!("if {}.{}", snippet(cx, op.span, "_"), good_method));
86+
});
87+
}
88+
89+
}
90+
}
91+
}

clippy_lints/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ pub mod format;
8282
pub mod formatting;
8383
pub mod functions;
8484
pub mod identity_op;
85+
pub mod if_let_redundant_pattern_matching;
8586
pub mod if_not_else;
8687
pub mod items_after_statements;
8788
pub mod len_zero;
@@ -257,6 +258,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
257258
reg.register_late_lint_pass(box eval_order_dependence::EvalOrderDependence);
258259
reg.register_late_lint_pass(box missing_doc::MissingDoc::new());
259260
reg.register_late_lint_pass(box ok_if_let::Pass);
261+
reg.register_late_lint_pass(box if_let_redundant_pattern_matching::Pass);
260262

261263
reg.register_lint_group("clippy_restrictions", vec![
262264
arithmetic::FLOAT_ARITHMETIC,
@@ -344,6 +346,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
344346
functions::NOT_UNSAFE_PTR_ARG_DEREF,
345347
functions::TOO_MANY_ARGUMENTS,
346348
identity_op::IDENTITY_OP,
349+
if_let_redundant_pattern_matching::IF_LET_REDUNDANT_PATTERN_MATCHING,
347350
len_zero::LEN_WITHOUT_IS_EMPTY,
348351
len_zero::LEN_ZERO,
349352
let_if_seq::USELESS_LET_IF_SEQ,

clippy_lints/src/methods.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ impl LateLintPass for Pass {
532532
lint_iter_nth(cx, expr, arglists[0], false);
533533
} else if let Some(arglists) = method_chain_args(expr, &["iter_mut", "nth"]) {
534534
lint_iter_nth(cx, expr, arglists[0], true);
535-
} else if let Some(_) = method_chain_args(expr, &["skip", "next"]) {
535+
} else if method_chain_args(expr, &["skip", "next"]).is_some() {
536536
lint_iter_skip_next(cx, expr);
537537
}
538538

@@ -796,7 +796,7 @@ fn lint_cstring_as_ptr(cx: &LateContext, expr: &hir::Expr, new: &hir::Expr, unwr
796796
// Type of MethodArgs is potentially a Vec
797797
fn lint_iter_nth(cx: &LateContext, expr: &hir::Expr, iter_args: &MethodArgs, is_mut: bool){
798798
let mut_str = if is_mut { "_mut" } else {""};
799-
let caller_type = if let Some(_) = derefs_to_slice(cx, &iter_args[0], cx.tcx.expr_ty(&iter_args[0])) {
799+
let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.tcx.expr_ty(&iter_args[0])).is_some() {
800800
"slice"
801801
}
802802
else if match_type(cx, cx.tcx.expr_ty(&iter_args[0]), &paths::VEC) {

clippy_lints/src/minmax.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ fn fetch_const(args: &[P<Expr>], m: MinMax) -> Option<(MinMax, Constant, &Expr)>
8585
return None;
8686
}
8787
if let Some(c) = constant_simple(&args[0]) {
88-
if let None = constant_simple(&args[1]) {
88+
if constant_simple(&args[1]).is_none() {
8989
// otherwise ignore
9090
Some((m, c, &args[1]))
9191
} else {

clippy_lints/src/ok_if_let.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@ impl LateLintPass for Pass {
3838
let MatchSource::IfLetDesugar { .. } = *source, //test if it is an If Let
3939
let ExprMethodCall(_, _, ref result_types) = op.node, //check is expr.ok() has type Result<T,E>.ok()
4040
let PatKind::TupleStruct(ref x, ref y, _) = body[0].pats[0].node, //get operation
41-
let Some(_) = method_chain_args(op, &["ok"]) //test to see if using ok() methoduse std::marker::Sized;
41+
method_chain_args(op, &["ok"]).is_some() //test to see if using ok() methoduse std::marker::Sized;
4242

4343
], {
4444
let is_result_type = match_type(cx, cx.tcx.expr_ty(&result_types[0]), &paths::RESULT);
4545
let some_expr_string = snippet(cx, y[0].span, "");
4646
if print::path_to_string(x) == "Some" && is_result_type {
4747
span_help_and_lint(cx, IF_LET_SOME_RESULT, expr.span,
4848
"Matching on `Some` with `ok()` is redundant",
49-
&format!("Consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string));
49+
&format!("Consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string));
5050
}
5151
}}
5252
}

clippy_lints/src/utils/paths.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ pub const MUTEX: [&'static str; 4] = ["std", "sync", "mutex", "Mutex"];
3434
pub const OPEN_OPTIONS: [&'static str; 3] = ["std", "fs", "OpenOptions"];
3535
pub const OPS_MODULE: [&'static str; 2] = ["core", "ops"];
3636
pub const OPTION: [&'static str; 3] = ["core", "option", "Option"];
37+
pub const OPTION_NONE: [&'static str; 4] = ["core", "option", "Option", "None"];
38+
pub const OPTION_SOME: [&'static str; 4] = ["core", "option", "Option", "Some"];
3739
pub const PTR_NULL: [&'static str; 2] = ["ptr", "null"];
3840
pub const PTR_NULL_MUT: [&'static str; 2] = ["ptr", "null_mut"];
3941
pub const RANGE: [&'static str; 3] = ["core", "ops", "Range"];
@@ -59,6 +61,8 @@ pub const REGEX_BYTES_SET_NEW: [&'static str; 5] = ["regex", "re_set", "bytes",
5961
pub const REGEX_NEW: [&'static str; 4] = ["regex", "re_unicode", "Regex", "new"];
6062
pub const REGEX_SET_NEW: [&'static str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
6163
pub const RESULT: [&'static str; 3] = ["core", "result", "Result"];
64+
pub const RESULT_ERR: [&'static str; 4] = ["core", "result", "Result", "Err"];
65+
pub const RESULT_OK: [&'static str; 4] = ["core", "result", "Result", "Ok"];
6266
pub const SERDE_DE_VISITOR: [&'static str; 3] = ["serde", "de", "Visitor"];
6367
pub const STRING: [&'static str; 3] = ["collections", "string", "String"];
6468
pub const TRANSMUTE: [&'static str; 4] = ["core", "intrinsics", "", "transmute"];
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#![feature(plugin)]
2+
3+
#![plugin(clippy)]
4+
#![deny(clippy)]
5+
#![deny(if_let_redundant_pattern_matching)]
6+
7+
8+
fn main() {
9+
if let Ok(_) = Ok::<i32, i32>(42) {}
10+
//~^ERROR redundant pattern matching, consider using `is_ok()`
11+
//~| HELP try this
12+
//~| SUGGESTION if Ok::<i32, i32>(42).is_ok() {
13+
14+
if let Err(_) = Err::<i32, i32>(42) {
15+
//~^ERROR redundant pattern matching, consider using `is_err()`
16+
//~| HELP try this
17+
//~| SUGGESTION if Err::<i32, i32>(42).is_err() {
18+
}
19+
20+
if let None = None::<()> {
21+
//~^ERROR redundant pattern matching, consider using `is_none()`
22+
//~| HELP try this
23+
//~| SUGGESTION if None::<()>.is_none() {
24+
}
25+
26+
if let Some(_) = Some(42) {
27+
//~^ERROR redundant pattern matching, consider using `is_some()`
28+
//~| HELP try this
29+
//~| SUGGESTION if Some(42).is_some() {
30+
}
31+
32+
if Ok::<i32, i32>(42).is_ok() {
33+
34+
}
35+
36+
if Err::<i32, i32>(42).is_err() {
37+
38+
}
39+
40+
if None::<i32>.is_none() {
41+
42+
}
43+
44+
if Some(42).is_some() {
45+
46+
}
47+
48+
if let Ok(x) = Ok::<i32,i32>(42) {
49+
println!("{}", x);
50+
}
51+
}
52+
53+

tests/compile-fail/matches.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
#![plugin(clippy)]
44
#![deny(clippy)]
5-
#![allow(unused)]
5+
#![allow(unused, if_let_redundant_pattern_matching)]
66
#![deny(single_match_else)]
77

88
use std::borrow::Cow;

0 commit comments

Comments
 (0)