mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-24 07:44:10 +00:00
Add lint for redundant pattern matching in iflet for Result/Option
This commit is contained in:
parent
502416fa78
commit
d213040381
@ -257,6 +257,7 @@ All notable changes to this project will be documented in this file.
|
||||
[`for_loop_over_option`]: https://github.com/Manishearth/rust-clippy/wiki#for_loop_over_option
|
||||
[`for_loop_over_result`]: https://github.com/Manishearth/rust-clippy/wiki#for_loop_over_result
|
||||
[`identity_op`]: https://github.com/Manishearth/rust-clippy/wiki#identity_op
|
||||
[`if_let_redundant_pattern_matching`]: https://github.com/Manishearth/rust-clippy/wiki#if_let_redundant_pattern_matching
|
||||
[`if_let_some_result`]: https://github.com/Manishearth/rust-clippy/wiki#if_let_some_result
|
||||
[`if_not_else`]: https://github.com/Manishearth/rust-clippy/wiki#if_not_else
|
||||
[`if_same_then_else`]: https://github.com/Manishearth/rust-clippy/wiki#if_same_then_else
|
||||
|
@ -174,10 +174,10 @@ You can check out this great service at [clippy.bashy.io](https://clippy.bashy.i
|
||||
|
||||
## Lints
|
||||
|
||||
There are 174 lints included in this crate:
|
||||
There are 175 lints included in this crate:
|
||||
|
||||
name | default | triggers on
|
||||
---------------------------------------------------------------------------------------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------
|
||||
-----------------------------------------------------------------------------------------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------
|
||||
[absurd_extreme_comparisons](https://github.com/Manishearth/rust-clippy/wiki#absurd_extreme_comparisons) | warn | a comparison with a maximum or minimum value that is always true or false
|
||||
[almost_swapped](https://github.com/Manishearth/rust-clippy/wiki#almost_swapped) | warn | `foo = bar; bar = foo` sequence
|
||||
[approx_constant](https://github.com/Manishearth/rust-clippy/wiki#approx_constant) | warn | the approximate of a known float constant (in `std::fXX::consts`)
|
||||
@ -231,6 +231,7 @@ name
|
||||
[for_loop_over_option](https://github.com/Manishearth/rust-clippy/wiki#for_loop_over_option) | warn | for-looping over an `Option`, which is more clearly expressed as an `if let`
|
||||
[for_loop_over_result](https://github.com/Manishearth/rust-clippy/wiki#for_loop_over_result) | warn | for-looping over a `Result`, which is more clearly expressed as an `if let`
|
||||
[identity_op](https://github.com/Manishearth/rust-clippy/wiki#identity_op) | warn | using identity operations, e.g. `x + 0` or `y / 1`
|
||||
[if_let_redundant_pattern_matching](https://github.com/Manishearth/rust-clippy/wiki#if_let_redundant_pattern_matching) | warn | use the proper utility function avoiding an `if let`
|
||||
[if_let_some_result](https://github.com/Manishearth/rust-clippy/wiki#if_let_some_result) | warn | usage of `ok()` in `if let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead
|
||||
[if_not_else](https://github.com/Manishearth/rust-clippy/wiki#if_not_else) | allow | `if` branches that could be swapped so no negation operation is necessary on the condition
|
||||
[if_same_then_else](https://github.com/Manishearth/rust-clippy/wiki#if_same_then_else) | warn | if with the same *then* and *else* blocks
|
||||
|
@ -76,7 +76,7 @@ fn check_lit(cx: &LateContext, lit: &Lit, e: &Expr) {
|
||||
}
|
||||
|
||||
fn check_known_consts(cx: &LateContext, e: &Expr, s: &str, module: &str) {
|
||||
if let Ok(_) = s.parse::<f64>() {
|
||||
if s.parse::<f64>().is_ok() {
|
||||
for &(constant, name, min_digits) in KNOWN_CONSTS {
|
||||
if is_approx_const(constant, s, min_digits) {
|
||||
span_lint(cx,
|
||||
|
@ -49,7 +49,7 @@ impl LintPass for Arithmetic {
|
||||
|
||||
impl LateLintPass for Arithmetic {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &hir::Expr) {
|
||||
if let Some(_) = self.span {
|
||||
if self.span.is_some() {
|
||||
return;
|
||||
}
|
||||
match expr.node {
|
||||
|
@ -322,7 +322,7 @@ fn check_doc(cx: &EarlyContext, valid_idents: &[String], docs: &[(&str, Span)])
|
||||
|
||||
if new_line {
|
||||
let mut lookup_parser = parser.clone();
|
||||
if let Some(_) = lookup_parser.find(|&(_, c)| c == ']') {
|
||||
if lookup_parser.any(|(_, c)| c == ']') {
|
||||
if let Some((_, ':')) = lookup_parser.next() {
|
||||
lookup_parser.next_line();
|
||||
parser = lookup_parser;
|
||||
|
91
clippy_lints/src/if_let_redundant_pattern_matching.rs
Normal file
91
clippy_lints/src/if_let_redundant_pattern_matching.rs
Normal file
@ -0,0 +1,91 @@
|
||||
use rustc::lint::*;
|
||||
use rustc::hir::*;
|
||||
use syntax::codemap::Span;
|
||||
use utils::{paths, span_lint_and_then, match_path, snippet};
|
||||
|
||||
/// **What it does:*** Lint for redundant pattern matching over `Result` or `Option`
|
||||
///
|
||||
/// **Why is this bad?** It's more concise and clear to just use the proper utility function
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// if let Ok(_) = Ok::<i32, i32>(42) {}
|
||||
/// if let Err(_) = Err::<i32, i32>(42) {}
|
||||
/// if let None = None::<()> {}
|
||||
/// if let Some(_) = Some(42) {}
|
||||
/// ```
|
||||
///
|
||||
/// The more idiomatic use would be:
|
||||
///
|
||||
/// ```rust
|
||||
/// if Ok::<i32, i32>(42).is_ok() {}
|
||||
/// if Err::<i32, i32>(42).is_err() {}
|
||||
/// if None::<()>.is_none() {}
|
||||
/// if Some(42).is_some() {}
|
||||
/// ```
|
||||
///
|
||||
declare_lint! {
|
||||
pub IF_LET_REDUNDANT_PATTERN_MATCHING,
|
||||
Warn,
|
||||
"use the proper utility function avoiding an `if let`"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Pass;
|
||||
|
||||
impl LintPass for Pass {
|
||||
fn get_lints(&self) -> LintArray {
|
||||
lint_array!(IF_LET_REDUNDANT_PATTERN_MATCHING)
|
||||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
|
||||
if let ExprMatch(ref op, ref arms, MatchSource::IfLetDesugar{..}) = expr.node {
|
||||
|
||||
if arms[0].pats.len() == 1 {
|
||||
|
||||
let good_method = match arms[0].pats[0].node {
|
||||
PatKind::TupleStruct(ref path, ref pats, _) if pats.len() == 1 && pats[0].node == PatKind::Wild => {
|
||||
|
||||
if match_path(path, &paths::RESULT_OK) {
|
||||
"is_ok()"
|
||||
} else if match_path(path, &paths::RESULT_ERR) {
|
||||
"is_err()"
|
||||
} else if match_path(path, &paths::OPTION_SOME) {
|
||||
"is_some()"
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::Path(_, ref path) if match_path(path, &paths::OPTION_NONE) => {
|
||||
"is_none()"
|
||||
}
|
||||
|
||||
_ => return
|
||||
};
|
||||
|
||||
span_lint_and_then(cx,
|
||||
IF_LET_REDUNDANT_PATTERN_MATCHING,
|
||||
arms[0].pats[0].span,
|
||||
&format!("redundant pattern matching, consider using `{}`", good_method),
|
||||
|db| {
|
||||
let span = Span {
|
||||
lo: expr.span.lo,
|
||||
hi: op.span.hi,
|
||||
expn_id: expr.span.expn_id,
|
||||
};
|
||||
db.span_suggestion(span,
|
||||
"try this",
|
||||
format!("if {}.{}", snippet(cx, op.span, "_"), good_method));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -82,6 +82,7 @@ pub mod format;
|
||||
pub mod formatting;
|
||||
pub mod functions;
|
||||
pub mod identity_op;
|
||||
pub mod if_let_redundant_pattern_matching;
|
||||
pub mod if_not_else;
|
||||
pub mod items_after_statements;
|
||||
pub mod len_zero;
|
||||
@ -257,6 +258,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
||||
reg.register_late_lint_pass(box eval_order_dependence::EvalOrderDependence);
|
||||
reg.register_late_lint_pass(box missing_doc::MissingDoc::new());
|
||||
reg.register_late_lint_pass(box ok_if_let::Pass);
|
||||
reg.register_late_lint_pass(box if_let_redundant_pattern_matching::Pass);
|
||||
|
||||
reg.register_lint_group("clippy_restrictions", vec![
|
||||
arithmetic::FLOAT_ARITHMETIC,
|
||||
@ -344,6 +346,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
||||
functions::NOT_UNSAFE_PTR_ARG_DEREF,
|
||||
functions::TOO_MANY_ARGUMENTS,
|
||||
identity_op::IDENTITY_OP,
|
||||
if_let_redundant_pattern_matching::IF_LET_REDUNDANT_PATTERN_MATCHING,
|
||||
len_zero::LEN_WITHOUT_IS_EMPTY,
|
||||
len_zero::LEN_ZERO,
|
||||
let_if_seq::USELESS_LET_IF_SEQ,
|
||||
|
@ -532,7 +532,7 @@ impl LateLintPass for Pass {
|
||||
lint_iter_nth(cx, expr, arglists[0], false);
|
||||
} else if let Some(arglists) = method_chain_args(expr, &["iter_mut", "nth"]) {
|
||||
lint_iter_nth(cx, expr, arglists[0], true);
|
||||
} else if let Some(_) = method_chain_args(expr, &["skip", "next"]) {
|
||||
} else if method_chain_args(expr, &["skip", "next"]).is_some() {
|
||||
lint_iter_skip_next(cx, expr);
|
||||
}
|
||||
|
||||
@ -796,7 +796,7 @@ fn lint_cstring_as_ptr(cx: &LateContext, expr: &hir::Expr, new: &hir::Expr, unwr
|
||||
// Type of MethodArgs is potentially a Vec
|
||||
fn lint_iter_nth(cx: &LateContext, expr: &hir::Expr, iter_args: &MethodArgs, is_mut: bool){
|
||||
let mut_str = if is_mut { "_mut" } else {""};
|
||||
let caller_type = if let Some(_) = derefs_to_slice(cx, &iter_args[0], cx.tcx.expr_ty(&iter_args[0])) {
|
||||
let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.tcx.expr_ty(&iter_args[0])).is_some() {
|
||||
"slice"
|
||||
}
|
||||
else if match_type(cx, cx.tcx.expr_ty(&iter_args[0]), &paths::VEC) {
|
||||
|
@ -85,7 +85,7 @@ fn fetch_const(args: &[P<Expr>], m: MinMax) -> Option<(MinMax, Constant, &Expr)>
|
||||
return None;
|
||||
}
|
||||
if let Some(c) = constant_simple(&args[0]) {
|
||||
if let None = constant_simple(&args[1]) {
|
||||
if constant_simple(&args[1]).is_none() {
|
||||
// otherwise ignore
|
||||
Some((m, c, &args[1]))
|
||||
} else {
|
||||
|
@ -38,7 +38,7 @@ impl LateLintPass for Pass {
|
||||
let MatchSource::IfLetDesugar { .. } = *source, //test if it is an If Let
|
||||
let ExprMethodCall(_, _, ref result_types) = op.node, //check is expr.ok() has type Result<T,E>.ok()
|
||||
let PatKind::TupleStruct(ref x, ref y, _) = body[0].pats[0].node, //get operation
|
||||
let Some(_) = method_chain_args(op, &["ok"]) //test to see if using ok() methoduse std::marker::Sized;
|
||||
method_chain_args(op, &["ok"]).is_some() //test to see if using ok() methoduse std::marker::Sized;
|
||||
|
||||
], {
|
||||
let is_result_type = match_type(cx, cx.tcx.expr_ty(&result_types[0]), &paths::RESULT);
|
||||
|
@ -34,6 +34,8 @@ pub const MUTEX: [&'static str; 4] = ["std", "sync", "mutex", "Mutex"];
|
||||
pub const OPEN_OPTIONS: [&'static str; 3] = ["std", "fs", "OpenOptions"];
|
||||
pub const OPS_MODULE: [&'static str; 2] = ["core", "ops"];
|
||||
pub const OPTION: [&'static str; 3] = ["core", "option", "Option"];
|
||||
pub const OPTION_NONE: [&'static str; 4] = ["core", "option", "Option", "None"];
|
||||
pub const OPTION_SOME: [&'static str; 4] = ["core", "option", "Option", "Some"];
|
||||
pub const PTR_NULL: [&'static str; 2] = ["ptr", "null"];
|
||||
pub const PTR_NULL_MUT: [&'static str; 2] = ["ptr", "null_mut"];
|
||||
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",
|
||||
pub const REGEX_NEW: [&'static str; 4] = ["regex", "re_unicode", "Regex", "new"];
|
||||
pub const REGEX_SET_NEW: [&'static str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
|
||||
pub const RESULT: [&'static str; 3] = ["core", "result", "Result"];
|
||||
pub const RESULT_ERR: [&'static str; 4] = ["core", "result", "Result", "Err"];
|
||||
pub const RESULT_OK: [&'static str; 4] = ["core", "result", "Result", "Ok"];
|
||||
pub const SERDE_DE_VISITOR: [&'static str; 3] = ["serde", "de", "Visitor"];
|
||||
pub const STRING: [&'static str; 3] = ["collections", "string", "String"];
|
||||
pub const TRANSMUTE: [&'static str; 4] = ["core", "intrinsics", "", "transmute"];
|
||||
|
53
tests/compile-fail/if_let_redundant_pattern_matching.rs
Normal file
53
tests/compile-fail/if_let_redundant_pattern_matching.rs
Normal file
@ -0,0 +1,53 @@
|
||||
#![feature(plugin)]
|
||||
|
||||
#![plugin(clippy)]
|
||||
#![deny(clippy)]
|
||||
#![deny(if_let_redundant_pattern_matching)]
|
||||
|
||||
|
||||
fn main() {
|
||||
if let Ok(_) = Ok::<i32, i32>(42) {}
|
||||
//~^ERROR redundant pattern matching, consider using `is_ok()`
|
||||
//~| HELP try this
|
||||
//~| SUGGESTION if Ok::<i32, i32>(42).is_ok() {
|
||||
|
||||
if let Err(_) = Err::<i32, i32>(42) {
|
||||
//~^ERROR redundant pattern matching, consider using `is_err()`
|
||||
//~| HELP try this
|
||||
//~| SUGGESTION if Err::<i32, i32>(42).is_err() {
|
||||
}
|
||||
|
||||
if let None = None::<()> {
|
||||
//~^ERROR redundant pattern matching, consider using `is_none()`
|
||||
//~| HELP try this
|
||||
//~| SUGGESTION if None::<()>.is_none() {
|
||||
}
|
||||
|
||||
if let Some(_) = Some(42) {
|
||||
//~^ERROR redundant pattern matching, consider using `is_some()`
|
||||
//~| HELP try this
|
||||
//~| SUGGESTION if Some(42).is_some() {
|
||||
}
|
||||
|
||||
if Ok::<i32, i32>(42).is_ok() {
|
||||
|
||||
}
|
||||
|
||||
if Err::<i32, i32>(42).is_err() {
|
||||
|
||||
}
|
||||
|
||||
if None::<i32>.is_none() {
|
||||
|
||||
}
|
||||
|
||||
if Some(42).is_some() {
|
||||
|
||||
}
|
||||
|
||||
if let Ok(x) = Ok::<i32,i32>(42) {
|
||||
println!("{}", x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#![plugin(clippy)]
|
||||
#![deny(clippy)]
|
||||
#![allow(unused)]
|
||||
#![allow(unused, if_let_redundant_pattern_matching)]
|
||||
#![deny(single_match_else)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
Loading…
Reference in New Issue
Block a user