Skip to content

#7594: Adding new 'while_let_some_result' linting #7608

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Sep 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ document.

[74d1561...master](https://github.com/rust-lang/rust-clippy/compare/74d1561...master)

### New Lints

* Renamed Lint: `if_let_some_result` is now called [`match_result_ok`]. Now also handles `while let` case.

## Rust 1.55

Current beta, release 2021-09-09
Expand Down Expand Up @@ -1491,7 +1495,7 @@ Released 2020-03-12
* `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
* [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
* [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
* [`if_let_some_result`] [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
* `if_let_some_result` [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)

### ICE fixes

Expand Down Expand Up @@ -2685,7 +2689,6 @@ Released 2018-09-13
[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
[`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching
[`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result
[`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else
[`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else
[`if_then_panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_panic
Expand Down Expand Up @@ -2775,6 +2778,7 @@ Released 2018-09-13
[`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items
[`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm
[`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
[`match_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_result_ok
[`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms
[`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
[`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
Expand Down
11 changes: 6 additions & 5 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,6 @@ mod future_not_send;
mod get_last_with_len;
mod identity_op;
mod if_let_mutex;
mod if_let_some_result;
mod if_not_else;
mod if_then_panic;
mod if_then_some_else_none;
Expand Down Expand Up @@ -264,6 +263,7 @@ mod map_clone;
mod map_err_ignore;
mod map_unit_fn;
mod match_on_vec_items;
mod match_result_ok;
mod matches;
mod mem_discriminant;
mod mem_forget;
Expand Down Expand Up @@ -658,7 +658,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
get_last_with_len::GET_LAST_WITH_LEN,
identity_op::IDENTITY_OP,
if_let_mutex::IF_LET_MUTEX,
if_let_some_result::IF_LET_SOME_RESULT,
if_not_else::IF_NOT_ELSE,
if_then_panic::IF_THEN_PANIC,
if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
Expand Down Expand Up @@ -728,6 +727,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
map_unit_fn::OPTION_MAP_UNIT_FN,
map_unit_fn::RESULT_MAP_UNIT_FN,
match_on_vec_items::MATCH_ON_VEC_ITEMS,
match_result_ok::MATCH_RESULT_OK,
matches::INFALLIBLE_DESTRUCTURING_MATCH,
matches::MATCH_AS_REF,
matches::MATCH_BOOL,
Expand Down Expand Up @@ -1259,7 +1259,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(get_last_with_len::GET_LAST_WITH_LEN),
LintId::of(identity_op::IDENTITY_OP),
LintId::of(if_let_mutex::IF_LET_MUTEX),
LintId::of(if_let_some_result::IF_LET_SOME_RESULT),
LintId::of(if_then_panic::IF_THEN_PANIC),
LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
LintId::of(infinite_iter::INFINITE_ITER),
Expand Down Expand Up @@ -1303,6 +1302,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(map_clone::MAP_CLONE),
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
LintId::of(match_result_ok::MATCH_RESULT_OK),
LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
LintId::of(matches::MATCH_AS_REF),
LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
Expand Down Expand Up @@ -1513,7 +1513,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(functions::DOUBLE_MUST_USE),
LintId::of(functions::MUST_USE_UNIT),
LintId::of(functions::RESULT_UNIT_ERR),
LintId::of(if_let_some_result::IF_LET_SOME_RESULT),
LintId::of(if_then_panic::IF_THEN_PANIC),
LintId::of(inherent_to_string::INHERENT_TO_STRING),
LintId::of(len_zero::COMPARISON_TO_EMPTY),
Expand All @@ -1530,6 +1529,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(manual_map::MANUAL_MAP),
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
LintId::of(map_clone::MAP_CLONE),
LintId::of(match_result_ok::MATCH_RESULT_OK),
LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
LintId::of(matches::MATCH_OVERLAPPING_ARM),
Expand Down Expand Up @@ -1985,7 +1985,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(missing_doc::MissingDoc::new()));
store.register_late_pass(|| Box::new(missing_inline::MissingInline));
store.register_late_pass(move || Box::new(exhaustive_items::ExhaustiveItems));
store.register_late_pass(|| Box::new(if_let_some_result::OkIfLet));
store.register_late_pass(|| Box::new(match_result_ok::MatchResultOk));
store.register_late_pass(|| Box::new(partialeq_ne_impl::PartialEqNeImpl));
store.register_late_pass(|| Box::new(unused_io_amount::UnusedIoAmount));
let enum_variant_size_threshold = conf.enum_variant_size_threshold;
Expand Down Expand Up @@ -2212,6 +2212,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion");
ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str");
ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok");

// uplifted lints
ls.register_renamed("clippy::invalid_ref", "invalid_value");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,58 +12,71 @@ use rustc_span::sym;

declare_clippy_lint! {
/// ### What it does
///* Checks for unnecessary `ok()` in if let.
/// Checks for unnecessary `ok()` in `while let`.
///
/// ### Why is this bad?
/// Calling `ok()` in if let is unnecessary, instead match
/// Calling `ok()` in `while let` is unnecessary, instead match
/// on `Ok(pat)`
///
/// ### Example
/// ```ignore
/// for i in iter {
/// if let Some(value) = i.parse().ok() {
/// vec.push(value)
/// }
/// while let Some(value) = iter.next().ok() {
/// vec.push(value)
/// }
/// ```
/// Could be written:
///
/// if let Some(valie) = iter.next().ok() {
/// vec.push(value)
/// }
/// ```
/// Use instead:
/// ```ignore
/// for i in iter {
/// if let Ok(value) = i.parse() {
/// vec.push(value)
/// }
/// while let Ok(value) = iter.next() {
/// vec.push(value)
/// }
///
/// if let Ok(value) = iter.next() {
/// vec.push_value)
/// }
/// ```
pub IF_LET_SOME_RESULT,
pub MATCH_RESULT_OK,
style,
"usage of `ok()` in `if let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead"
"usage of `ok()` in `let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead"
}

declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]);
declare_lint_pass!(MatchResultOk => [MATCH_RESULT_OK]);

impl<'tcx> LateLintPass<'tcx> for OkIfLet {
impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! { //begin checking variables
if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr);
let (let_pat, let_expr, ifwhile) =
if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr) {
(let_pat, let_expr, "if")
} else if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
(let_pat, let_expr, "while")
} else {
return;
};

if_chain! {
if let ExprKind::MethodCall(_, ok_span, [ref result_types_0, ..], _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = let_pat.kind; //get operation
if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::result_type);
if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";

then {

let mut applicability = Applicability::MachineApplicable;
let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability);
let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_span), "", &mut applicability);
let sugg = format!(
"if let Ok({}) = {}",
"{} let Ok({}) = {}",
ifwhile,
some_expr_string,
trimmed_ok.trim().trim_end_matches('.'),
);
span_lint_and_sugg(
cx,
IF_LET_SOME_RESULT,
MATCH_RESULT_OK,
expr.span.with_hi(let_expr.span.hi()),
"matching on `Some` with `ok()` is redundant",
&format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string),
Expand Down
28 changes: 0 additions & 28 deletions tests/ui/if_let_some_result.fixed

This file was deleted.

28 changes: 0 additions & 28 deletions tests/ui/if_let_some_result.rs

This file was deleted.

63 changes: 63 additions & 0 deletions tests/ui/match_result_ok.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// run-rustfix

#![warn(clippy::match_result_ok)]
#![allow(clippy::boxed_local)]
#![allow(dead_code)]

// Checking `if` cases

fn str_to_int(x: &str) -> i32 {
if let Ok(y) = x.parse() { y } else { 0 }
}

fn str_to_int_ok(x: &str) -> i32 {
if let Ok(y) = x.parse() { y } else { 0 }
}

#[rustfmt::skip]
fn strange_some_no_else(x: &str) -> i32 {
{
if let Ok(y) = x . parse() {
return y;
};
0
}
}

// Checking `while` cases

struct Wat {
counter: i32,
}

impl Wat {
fn next(&mut self) -> Result<i32, &str> {
self.counter += 1;
if self.counter < 5 {
Ok(self.counter)
} else {
Err("Oh no")
}
}
}

fn base_1(x: i32) {
let mut wat = Wat { counter: x };
while let Ok(a) = wat.next() {
println!("{}", a);
}
}

fn base_2(x: i32) {
let mut wat = Wat { counter: x };
while let Ok(a) = wat.next() {
println!("{}", a);
}
}

fn base_3(test_func: Box<Result<i32, &str>>) {
// Expected to stay as is
while let Some(_b) = test_func.ok() {}
}

fn main() {}
63 changes: 63 additions & 0 deletions tests/ui/match_result_ok.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// run-rustfix

#![warn(clippy::match_result_ok)]
#![allow(clippy::boxed_local)]
#![allow(dead_code)]

// Checking `if` cases

fn str_to_int(x: &str) -> i32 {
if let Some(y) = x.parse().ok() { y } else { 0 }
}

fn str_to_int_ok(x: &str) -> i32 {
if let Ok(y) = x.parse() { y } else { 0 }
}

#[rustfmt::skip]
fn strange_some_no_else(x: &str) -> i32 {
{
if let Some(y) = x . parse() . ok () {
return y;
};
0
}
}

// Checking `while` cases

struct Wat {
counter: i32,
}

impl Wat {
fn next(&mut self) -> Result<i32, &str> {
self.counter += 1;
if self.counter < 5 {
Ok(self.counter)
} else {
Err("Oh no")
}
}
}

fn base_1(x: i32) {
let mut wat = Wat { counter: x };
while let Some(a) = wat.next().ok() {
println!("{}", a);
}
}

fn base_2(x: i32) {
let mut wat = Wat { counter: x };
while let Ok(a) = wat.next() {
println!("{}", a);
}
}

fn base_3(test_func: Box<Result<i32, &str>>) {
// Expected to stay as is
while let Some(_b) = test_func.ok() {}
}

fn main() {}
Loading