Auto merge of #6591 - camsteffen:manual-filter-map, r=llogiq
`manual_filter_map` and `manual_find_map` changelog: Add `manual_filter_map` and replace `find_map` with `manual_find_map` Replaces #6453 Fixes #3188 Fixes #4193 ~Depends on #6567 (to fix an internal lint false positive)~ This replaces `filter_map` and `find_map` with `manual_filter_map` and `manual_find_map` respectively. However, `filter_map` is left in place since it is used for a variety of other cases. See discussion in #6453.
This commit is contained in:
commit
3c3f4a75b4
@ -2035,6 +2035,8 @@ Released 2018-09-13
|
||||
[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
|
||||
[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
|
||||
[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
|
||||
[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
|
||||
[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
|
||||
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
|
||||
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
|
||||
[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
|
||||
|
@ -1,5 +1,3 @@
|
||||
#![allow(clippy::filter_map)]
|
||||
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
|
@ -166,3 +166,8 @@ declare_deprecated_lint! {
|
||||
pub PANIC_PARAMS,
|
||||
"this lint has been uplifted to rustc and is now called `panic_fmt`"
|
||||
}
|
||||
|
||||
declare_deprecated_lint! {
|
||||
pub FIND_MAP,
|
||||
"this lint is replaced by `manual_find_map`, a more specific lint"
|
||||
}
|
||||
|
@ -505,6 +505,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
"clippy::panic_params",
|
||||
"this lint has been uplifted to rustc and is now called `panic_fmt`",
|
||||
);
|
||||
store.register_removed(
|
||||
"clippy::find_map",
|
||||
"this lint is replaced by `manual_find_map`, a more specific lint",
|
||||
);
|
||||
// end deprecated lints, do not remove this comment, it’s used in `update_lints`
|
||||
|
||||
// begin register lints, do not remove this comment, it’s used in `update_lints`
|
||||
@ -732,7 +736,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&methods::FILTER_MAP,
|
||||
&methods::FILTER_MAP_NEXT,
|
||||
&methods::FILTER_NEXT,
|
||||
&methods::FIND_MAP,
|
||||
&methods::FLAT_MAP_IDENTITY,
|
||||
&methods::FROM_ITER_INSTEAD_OF_COLLECT,
|
||||
&methods::GET_UNWRAP,
|
||||
@ -745,6 +748,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&methods::ITER_NTH,
|
||||
&methods::ITER_NTH_ZERO,
|
||||
&methods::ITER_SKIP_NEXT,
|
||||
&methods::MANUAL_FILTER_MAP,
|
||||
&methods::MANUAL_FIND_MAP,
|
||||
&methods::MANUAL_SATURATING_ARITHMETIC,
|
||||
&methods::MAP_COLLECT_RESULT_UNIT,
|
||||
&methods::MAP_FLATTEN,
|
||||
@ -1331,7 +1336,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&matches::SINGLE_MATCH_ELSE),
|
||||
LintId::of(&methods::FILTER_MAP),
|
||||
LintId::of(&methods::FILTER_MAP_NEXT),
|
||||
LintId::of(&methods::FIND_MAP),
|
||||
LintId::of(&methods::INEFFICIENT_TO_STRING),
|
||||
LintId::of(&methods::MAP_FLATTEN),
|
||||
LintId::of(&methods::MAP_UNWRAP_OR),
|
||||
@ -1526,6 +1530,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&methods::ITER_NTH),
|
||||
LintId::of(&methods::ITER_NTH_ZERO),
|
||||
LintId::of(&methods::ITER_SKIP_NEXT),
|
||||
LintId::of(&methods::MANUAL_FILTER_MAP),
|
||||
LintId::of(&methods::MANUAL_FIND_MAP),
|
||||
LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC),
|
||||
LintId::of(&methods::MAP_COLLECT_RESULT_UNIT),
|
||||
LintId::of(&methods::NEW_RET_NO_SELF),
|
||||
@ -1823,6 +1829,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&methods::FILTER_NEXT),
|
||||
LintId::of(&methods::FLAT_MAP_IDENTITY),
|
||||
LintId::of(&methods::INSPECT_FOR_EACH),
|
||||
LintId::of(&methods::MANUAL_FILTER_MAP),
|
||||
LintId::of(&methods::MANUAL_FIND_MAP),
|
||||
LintId::of(&methods::OPTION_AS_REF_DEREF),
|
||||
LintId::of(&methods::SEARCH_IS_SOME),
|
||||
LintId::of(&methods::SKIP_WHILE_NEXT),
|
||||
|
@ -1069,7 +1069,6 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>(
|
||||
) -> impl Iterator<Item = Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>> + 'c {
|
||||
// As the `filter` and `map` below do different things, I think putting together
|
||||
// just increases complexity. (cc #3188 and #4193)
|
||||
#[allow(clippy::filter_map)]
|
||||
stmts
|
||||
.iter()
|
||||
.filter_map(move |stmt| match stmt.kind {
|
||||
|
@ -15,7 +15,8 @@ use if_chain::if_chain;
|
||||
use rustc_ast::ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{TraitItem, TraitItemKind};
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{Expr, ExprKind, PatKind, QPath, TraitItem, TraitItemKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, TraitRef, Ty, TyS};
|
||||
@ -450,6 +451,58 @@ declare_clippy_lint! {
|
||||
"using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `_.filter(_).map(_)` that can be written more simply
|
||||
/// as `filter_map(_)`.
|
||||
///
|
||||
/// **Why is this bad?** Redundant code in the `filter` and `map` operations is poor style and
|
||||
/// less performant.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// (0_i32..10)
|
||||
/// .filter(|n| n.checked_add(1).is_some())
|
||||
/// .map(|n| n.checked_add(1).unwrap());
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// ```rust
|
||||
/// (0_i32..10).filter_map(|n| n.checked_add(1));
|
||||
/// ```
|
||||
pub MANUAL_FILTER_MAP,
|
||||
complexity,
|
||||
"using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `_.find(_).map(_)` that can be written more simply
|
||||
/// as `find_map(_)`.
|
||||
///
|
||||
/// **Why is this bad?** Redundant code in the `find` and `map` operations is poor style and
|
||||
/// less performant.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// (0_i32..10)
|
||||
/// .find(|n| n.checked_add(1).is_some())
|
||||
/// .map(|n| n.checked_add(1).unwrap());
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// ```rust
|
||||
/// (0_i32..10).find_map(|n| n.checked_add(1));
|
||||
/// ```
|
||||
pub MANUAL_FIND_MAP,
|
||||
complexity,
|
||||
"using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `_.filter_map(_).next()`.
|
||||
///
|
||||
@ -494,28 +547,6 @@ declare_clippy_lint! {
|
||||
"call to `flat_map` where `flatten` is sufficient"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `_.find(_).map(_)`.
|
||||
///
|
||||
/// **Why is this bad?** Readability, this can be written more concisely as
|
||||
/// `_.find_map(_)`.
|
||||
///
|
||||
/// **Known problems:** Often requires a condition + Option/Iterator creation
|
||||
/// inside the closure.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// (0..3).find(|x| *x == 2).map(|x| x * 2);
|
||||
/// ```
|
||||
/// Can be written as
|
||||
/// ```rust
|
||||
/// (0..3).find_map(|x| if x == 2 { Some(x * 2) } else { None });
|
||||
/// ```
|
||||
pub FIND_MAP,
|
||||
pedantic,
|
||||
"using a combination of `find` and `map` can usually be written as a single method call"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for an iterator or string search (such as `find()`,
|
||||
/// `position()`, or `rposition()`) followed by a call to `is_some()`.
|
||||
@ -1473,9 +1504,10 @@ impl_lint_pass!(Methods => [
|
||||
FILTER_NEXT,
|
||||
SKIP_WHILE_NEXT,
|
||||
FILTER_MAP,
|
||||
MANUAL_FILTER_MAP,
|
||||
MANUAL_FIND_MAP,
|
||||
FILTER_MAP_NEXT,
|
||||
FLAT_MAP_IDENTITY,
|
||||
FIND_MAP,
|
||||
MAP_FLATTEN,
|
||||
ITERATOR_STEP_BY_ZERO,
|
||||
ITER_NEXT_SLICE,
|
||||
@ -1540,10 +1572,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]),
|
||||
["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]),
|
||||
["next", "iter"] => lint_iter_next(cx, expr, arg_lists[1]),
|
||||
["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["map", "filter"] => lint_filter_map(cx, expr, false),
|
||||
["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1], self.msrv.as_ref()),
|
||||
["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["map", "find"] => lint_filter_map(cx, expr, true),
|
||||
["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["flat_map", ..] => lint_flat_map_identity(cx, expr, arg_lists[0], method_spans[0]),
|
||||
@ -2988,18 +3020,79 @@ fn lint_skip_while_next<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
/// lint use of `filter().map()` for `Iterators`
|
||||
fn lint_filter_map<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
_filter_args: &'tcx [hir::Expr<'_>],
|
||||
_map_args: &'tcx [hir::Expr<'_>],
|
||||
) {
|
||||
// lint if caller of `.filter().map()` is an Iterator
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR) {
|
||||
let msg = "called `filter(..).map(..)` on an `Iterator`";
|
||||
let hint = "this is more succinctly expressed by calling `.filter_map(..)` instead";
|
||||
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
|
||||
/// lint use of `filter().map()` or `find().map()` for `Iterators`
|
||||
fn lint_filter_map<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_find: bool) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(_, _, [map_recv, map_arg], map_span) = expr.kind;
|
||||
if let ExprKind::MethodCall(_, _, [_, filter_arg], filter_span) = map_recv.kind;
|
||||
if match_trait_method(cx, map_recv, &paths::ITERATOR);
|
||||
|
||||
// filter(|x| ...is_some())...
|
||||
if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind;
|
||||
let filter_body = cx.tcx.hir().body(filter_body_id);
|
||||
if let [filter_param] = filter_body.params;
|
||||
// optional ref pattern: `filter(|&x| ..)`
|
||||
let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind {
|
||||
(ref_pat, true)
|
||||
} else {
|
||||
(filter_param.pat, false)
|
||||
};
|
||||
// closure ends with is_some() or is_ok()
|
||||
if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind;
|
||||
if let ExprKind::MethodCall(path, _, [filter_arg], _) = filter_body.value.kind;
|
||||
if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).ty_adt_def();
|
||||
if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::option_type, opt_ty.did) {
|
||||
Some(false)
|
||||
} else if cx.tcx.is_diagnostic_item(sym::result_type, opt_ty.did) {
|
||||
Some(true)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" };
|
||||
|
||||
// ...map(|x| ...unwrap())
|
||||
if let ExprKind::Closure(_, _, map_body_id, ..) = map_arg.kind;
|
||||
let map_body = cx.tcx.hir().body(map_body_id);
|
||||
if let [map_param] = map_body.params;
|
||||
if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind;
|
||||
// closure ends with expect() or unwrap()
|
||||
if let ExprKind::MethodCall(seg, _, [map_arg, ..], _) = map_body.value.kind;
|
||||
if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or);
|
||||
|
||||
let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
|
||||
// in `filter(|x| ..)`, replace `*x` with `x`
|
||||
let a_path = if_chain! {
|
||||
if !is_filter_param_ref;
|
||||
if let ExprKind::Unary(UnOp::UnDeref, expr_path) = a.kind;
|
||||
then { expr_path } else { a }
|
||||
};
|
||||
// let the filter closure arg and the map closure arg be equal
|
||||
if_chain! {
|
||||
if let ExprKind::Path(QPath::Resolved(None, a_path)) = a_path.kind;
|
||||
if let ExprKind::Path(QPath::Resolved(None, b_path)) = b.kind;
|
||||
if a_path.res == Res::Local(filter_param_id);
|
||||
if b_path.res == Res::Local(map_param_id);
|
||||
if TyS::same_type(cx.typeck_results().expr_ty_adjusted(a), cx.typeck_results().expr_ty_adjusted(b));
|
||||
then {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
};
|
||||
if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg);
|
||||
then {
|
||||
let span = filter_span.to(map_span);
|
||||
let (filter_name, lint) = if is_find {
|
||||
("find", MANUAL_FIND_MAP)
|
||||
} else {
|
||||
("filter", MANUAL_FILTER_MAP)
|
||||
};
|
||||
let msg = format!("`{}(..).map(..)` can be simplified as `{0}_map(..)`", filter_name);
|
||||
let to_opt = if is_result { ".ok()" } else { "" };
|
||||
let sugg = format!("{}_map(|{}| {}{})", filter_name, map_param_ident,
|
||||
snippet(cx, map_arg.span, ".."), to_opt);
|
||||
span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3037,21 +3130,6 @@ fn lint_filter_map_next<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
/// lint use of `find().map()` for `Iterators`
|
||||
fn lint_find_map<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
_find_args: &'tcx [hir::Expr<'_>],
|
||||
map_args: &'tcx [hir::Expr<'_>],
|
||||
) {
|
||||
// lint if caller of `.filter().map()` is an Iterator
|
||||
if match_trait_method(cx, &map_args[0], &paths::ITERATOR) {
|
||||
let msg = "called `find(..).map(..)` on an `Iterator`";
|
||||
let hint = "this is more succinctly expressed by calling `.find_map(..)` instead";
|
||||
span_lint_and_help(cx, FIND_MAP, expr.span, msg, None, hint);
|
||||
}
|
||||
}
|
||||
|
||||
/// lint use of `filter_map().map()` for `Iterators`
|
||||
fn lint_filter_map_map<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
@ -3059,7 +3137,7 @@ fn lint_filter_map_map<'tcx>(
|
||||
_filter_args: &'tcx [hir::Expr<'_>],
|
||||
_map_args: &'tcx [hir::Expr<'_>],
|
||||
) {
|
||||
// lint if caller of `.filter().map()` is an Iterator
|
||||
// lint if caller of `.filter_map().map()` is an Iterator
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR) {
|
||||
let msg = "called `filter_map(..).map(..)` on an `Iterator`";
|
||||
let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead";
|
||||
|
@ -24,6 +24,7 @@ pub struct SpanlessEq<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
|
||||
allow_side_effects: bool,
|
||||
expr_fallback: Option<Box<dyn Fn(&Expr<'_>, &Expr<'_>) -> bool + 'a>>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
||||
@ -32,6 +33,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
||||
cx,
|
||||
maybe_typeck_results: cx.maybe_typeck_results(),
|
||||
allow_side_effects: true,
|
||||
expr_fallback: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,6 +45,13 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expr_fallback(self, expr_fallback: impl Fn(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self {
|
||||
Self {
|
||||
expr_fallback: Some(Box::new(expr_fallback)),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether two statements are the same.
|
||||
pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
|
||||
match (&left.kind, &right.kind) {
|
||||
@ -81,7 +90,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
match (&reduce_exprkind(&left.kind), &reduce_exprkind(&right.kind)) {
|
||||
let is_eq = match (&reduce_exprkind(&left.kind), &reduce_exprkind(&right.kind)) {
|
||||
(&ExprKind::AddrOf(lb, l_mut, ref le), &ExprKind::AddrOf(rb, r_mut, ref re)) => {
|
||||
lb == rb && l_mut == r_mut && self.eq_expr(le, re)
|
||||
},
|
||||
@ -158,7 +167,8 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
||||
(&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r),
|
||||
(&ExprKind::DropTemps(ref le), &ExprKind::DropTemps(ref re)) => self.eq_expr(le, re),
|
||||
_ => false,
|
||||
}
|
||||
};
|
||||
is_eq || self.expr_fallback.as_ref().map_or(false, |f| f(left, right))
|
||||
}
|
||||
|
||||
fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {
|
||||
|
@ -1,12 +1,3 @@
|
||||
error: called `filter(..).map(..)` on an `Iterator`
|
||||
--> $DIR/filter_methods.rs:6:21
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::filter-map` implied by `-D warnings`
|
||||
= help: this is more succinctly expressed by calling `.filter_map(..)` instead
|
||||
|
||||
error: called `filter(..).flat_map(..)` on an `Iterator`
|
||||
--> $DIR/filter_methods.rs:8:21
|
||||
|
|
||||
@ -17,6 +8,7 @@ LL | | .filter(|&x| x == 0)
|
||||
LL | | .flat_map(|x| x.checked_mul(2))
|
||||
| |_______________________________________^
|
||||
|
|
||||
= note: `-D clippy::filter-map` implied by `-D warnings`
|
||||
= help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()`
|
||||
|
||||
error: called `filter_map(..).flat_map(..)` on an `Iterator`
|
||||
@ -43,5 +35,5 @@ LL | | .map(|x| x.checked_mul(2))
|
||||
|
|
||||
= help: this is more succinctly expressed by only calling `.filter_map(..)` instead
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
|
@ -1,26 +0,0 @@
|
||||
error: called `find(..).map(..)` on an `Iterator`
|
||||
--> $DIR/find_map.rs:20:26
|
||||
|
|
||||
LL | let _: Option<i32> = a.iter().find(|s| s.parse::<i32>().is_ok()).map(|s| s.parse().unwrap());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::find-map` implied by `-D warnings`
|
||||
= help: this is more succinctly expressed by calling `.find_map(..)` instead
|
||||
|
||||
error: called `find(..).map(..)` on an `Iterator`
|
||||
--> $DIR/find_map.rs:23:29
|
||||
|
|
||||
LL | let _: Option<Flavor> = desserts_of_the_week
|
||||
| _____________________________^
|
||||
LL | | .iter()
|
||||
LL | | .find(|dessert| match *dessert {
|
||||
LL | | Dessert::Cake(_) => true,
|
||||
... |
|
||||
LL | | _ => unreachable!(),
|
||||
LL | | });
|
||||
| |__________^
|
||||
|
|
||||
= help: this is more succinctly expressed by calling `.find_map(..)` instead
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
37
tests/ui/manual_filter_map.fixed
Normal file
37
tests/ui/manual_filter_map.fixed
Normal file
@ -0,0 +1,37 @@
|
||||
// run-rustfix
|
||||
#![allow(dead_code)]
|
||||
#![warn(clippy::manual_filter_map)]
|
||||
#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
|
||||
|
||||
fn main() {
|
||||
// is_some(), unwrap()
|
||||
let _ = (0..).filter_map(|a| to_opt(a));
|
||||
|
||||
// ref pattern, expect()
|
||||
let _ = (0..).filter_map(|a| to_opt(a));
|
||||
|
||||
// is_ok(), unwrap_or()
|
||||
let _ = (0..).filter_map(|a| to_res(a).ok());
|
||||
}
|
||||
|
||||
fn no_lint() {
|
||||
// no shared code
|
||||
let _ = (0..).filter(|n| *n > 1).map(|n| n + 1);
|
||||
|
||||
// very close but different since filter() provides a reference
|
||||
let _ = (0..).filter(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap());
|
||||
|
||||
// similar but different
|
||||
let _ = (0..).filter(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap());
|
||||
let _ = (0..)
|
||||
.filter(|n| to_opt(n).map(|n| n + 1).is_some())
|
||||
.map(|a| to_opt(a).unwrap());
|
||||
}
|
||||
|
||||
fn to_opt<T>(_: T) -> Option<T> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn to_res<T>(_: T) -> Result<T, ()> {
|
||||
unimplemented!()
|
||||
}
|
37
tests/ui/manual_filter_map.rs
Normal file
37
tests/ui/manual_filter_map.rs
Normal file
@ -0,0 +1,37 @@
|
||||
// run-rustfix
|
||||
#![allow(dead_code)]
|
||||
#![warn(clippy::manual_filter_map)]
|
||||
#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
|
||||
|
||||
fn main() {
|
||||
// is_some(), unwrap()
|
||||
let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
|
||||
|
||||
// ref pattern, expect()
|
||||
let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
|
||||
|
||||
// is_ok(), unwrap_or()
|
||||
let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
|
||||
}
|
||||
|
||||
fn no_lint() {
|
||||
// no shared code
|
||||
let _ = (0..).filter(|n| *n > 1).map(|n| n + 1);
|
||||
|
||||
// very close but different since filter() provides a reference
|
||||
let _ = (0..).filter(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap());
|
||||
|
||||
// similar but different
|
||||
let _ = (0..).filter(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap());
|
||||
let _ = (0..)
|
||||
.filter(|n| to_opt(n).map(|n| n + 1).is_some())
|
||||
.map(|a| to_opt(a).unwrap());
|
||||
}
|
||||
|
||||
fn to_opt<T>(_: T) -> Option<T> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn to_res<T>(_: T) -> Result<T, ()> {
|
||||
unimplemented!()
|
||||
}
|
22
tests/ui/manual_filter_map.stderr
Normal file
22
tests/ui/manual_filter_map.stderr
Normal file
@ -0,0 +1,22 @@
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:8:19
|
||||
|
|
||||
LL | let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_opt(a))`
|
||||
|
|
||||
= note: `-D clippy::manual-filter-map` implied by `-D warnings`
|
||||
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:11:19
|
||||
|
|
||||
LL | let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_opt(a))`
|
||||
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:14:19
|
||||
|
|
||||
LL | let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_res(a).ok())`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
37
tests/ui/manual_find_map.fixed
Normal file
37
tests/ui/manual_find_map.fixed
Normal file
@ -0,0 +1,37 @@
|
||||
// run-rustfix
|
||||
#![allow(dead_code)]
|
||||
#![warn(clippy::manual_find_map)]
|
||||
#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
|
||||
|
||||
fn main() {
|
||||
// is_some(), unwrap()
|
||||
let _ = (0..).find_map(|a| to_opt(a));
|
||||
|
||||
// ref pattern, expect()
|
||||
let _ = (0..).find_map(|a| to_opt(a));
|
||||
|
||||
// is_ok(), unwrap_or()
|
||||
let _ = (0..).find_map(|a| to_res(a).ok());
|
||||
}
|
||||
|
||||
fn no_lint() {
|
||||
// no shared code
|
||||
let _ = (0..).filter(|n| *n > 1).map(|n| n + 1);
|
||||
|
||||
// very close but different since filter() provides a reference
|
||||
let _ = (0..).find(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap());
|
||||
|
||||
// similar but different
|
||||
let _ = (0..).find(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap());
|
||||
let _ = (0..)
|
||||
.find(|n| to_opt(n).map(|n| n + 1).is_some())
|
||||
.map(|a| to_opt(a).unwrap());
|
||||
}
|
||||
|
||||
fn to_opt<T>(_: T) -> Option<T> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn to_res<T>(_: T) -> Result<T, ()> {
|
||||
unimplemented!()
|
||||
}
|
37
tests/ui/manual_find_map.rs
Normal file
37
tests/ui/manual_find_map.rs
Normal file
@ -0,0 +1,37 @@
|
||||
// run-rustfix
|
||||
#![allow(dead_code)]
|
||||
#![warn(clippy::manual_find_map)]
|
||||
#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure
|
||||
|
||||
fn main() {
|
||||
// is_some(), unwrap()
|
||||
let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
|
||||
|
||||
// ref pattern, expect()
|
||||
let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
|
||||
|
||||
// is_ok(), unwrap_or()
|
||||
let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
|
||||
}
|
||||
|
||||
fn no_lint() {
|
||||
// no shared code
|
||||
let _ = (0..).filter(|n| *n > 1).map(|n| n + 1);
|
||||
|
||||
// very close but different since filter() provides a reference
|
||||
let _ = (0..).find(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap());
|
||||
|
||||
// similar but different
|
||||
let _ = (0..).find(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap());
|
||||
let _ = (0..)
|
||||
.find(|n| to_opt(n).map(|n| n + 1).is_some())
|
||||
.map(|a| to_opt(a).unwrap());
|
||||
}
|
||||
|
||||
fn to_opt<T>(_: T) -> Option<T> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn to_res<T>(_: T) -> Result<T, ()> {
|
||||
unimplemented!()
|
||||
}
|
22
tests/ui/manual_find_map.stderr
Normal file
22
tests/ui/manual_find_map.stderr
Normal file
@ -0,0 +1,22 @@
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_find_map.rs:8:19
|
||||
|
|
||||
LL | let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_opt(a))`
|
||||
|
|
||||
= note: `-D clippy::manual-find-map` implied by `-D warnings`
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_find_map.rs:11:19
|
||||
|
|
||||
LL | let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_opt(a))`
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_find_map.rs:14:19
|
||||
|
|
||||
LL | let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_res(a).ok())`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
Loading…
x
Reference in New Issue
Block a user