Auto merge of #10949 - y21:issue8010, r=Alexendoo
[`manual_filter_map`]: lint on `matches` and pattern matching Fixes #8010 Previously this lint only worked specifically for a very limited set of methods on the filter call (`.filter(|opt| opt.is_some())` and `.filter(|res| res.is_ok())`). This PR extends it to also recognize `matches!` in the `filter` and pattern matching with `if let` or `match` in the `map`. Example: ```rs enum Enum { A(i32), B, } let _ = [Enum::A(123), Enum::B].into_iter() .filter(|x| matches!(x, Enum::A(_))) .map(|x| if let Enum::A(s) = x { s } else { unreachable!() }); ``` Now suggests: ```diff - .filter(|x| matches!(x, Enum::A(_))).map(if let Enum::A(s) = x { s } else { unreachable!() }) + .filter_map(|x| match x { Enum::A(s) => Some(s), _ => None }) ``` Adding this required a somewhat large change in code because it originally seemed to be specifically written with only method calls in the filter in mind, and `matches!` has different behavior in the map, so this new setup should make it possible to support more "generic" cases that need different handling for the filter and map calls. changelog: [`manual_filter_map`]: lint on `matches` and pattern matching (and some internal refactoring)
This commit is contained in:
commit
0b63e95dce
@ -1,7 +1,9 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::macros::{is_panic, root_macro_call};
|
||||
use clippy_utils::source::{indent_of, reindent_multiline, snippet};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, SpanlessEq};
|
||||
use clippy_utils::{higher, is_trait_method, path_to_local_id, peel_blocks, SpanlessEq};
|
||||
use hir::{Body, HirId, MatchSource, Pat};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
@ -10,7 +12,7 @@
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::adjustment::Adjust;
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_span::symbol::{sym, Ident, Symbol};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::{MANUAL_FILTER_MAP, MANUAL_FIND_MAP, OPTION_FILTER_MAP};
|
||||
@ -48,6 +50,214 @@ fn is_option_filter_map(cx: &LateContext<'_>, filter_arg: &hir::Expr<'_>, map_ar
|
||||
is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some))
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum OffendingFilterExpr<'tcx> {
|
||||
/// `.filter(|opt| opt.is_some())`
|
||||
IsSome {
|
||||
/// The receiver expression
|
||||
receiver: &'tcx Expr<'tcx>,
|
||||
/// If `Some`, then this contains the span of an expression that possibly contains side
|
||||
/// effects: `.filter(|opt| side_effect(opt).is_some())`
|
||||
/// ^^^^^^^^^^^^^^^^
|
||||
///
|
||||
/// We will use this later for warning the user that the suggested fix may change
|
||||
/// the behavior.
|
||||
side_effect_expr_span: Option<Span>,
|
||||
},
|
||||
/// `.filter(|res| res.is_ok())`
|
||||
IsOk {
|
||||
/// The receiver expression
|
||||
receiver: &'tcx Expr<'tcx>,
|
||||
/// See `IsSome`
|
||||
side_effect_expr_span: Option<Span>,
|
||||
},
|
||||
/// `.filter(|enum| matches!(enum, Enum::A(_)))`
|
||||
Matches {
|
||||
/// The DefId of the variant being matched
|
||||
variant_def_id: hir::def_id::DefId,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum CalledMethod {
|
||||
OptionIsSome,
|
||||
ResultIsOk,
|
||||
}
|
||||
|
||||
/// The result of checking a `map` call, returned by `OffendingFilterExpr::check_map_call`
|
||||
#[derive(Debug)]
|
||||
enum CheckResult<'tcx> {
|
||||
Method {
|
||||
map_arg: &'tcx Expr<'tcx>,
|
||||
/// The method that was called inside of `filter`
|
||||
method: CalledMethod,
|
||||
/// See `OffendingFilterExpr::IsSome`
|
||||
side_effect_expr_span: Option<Span>,
|
||||
},
|
||||
PatternMatching {
|
||||
/// The span of the variant being matched
|
||||
/// if let Some(s) = enum
|
||||
/// ^^^^^^^
|
||||
variant_span: Span,
|
||||
/// if let Some(s) = enum
|
||||
/// ^
|
||||
variant_ident: Ident,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'tcx> OffendingFilterExpr<'tcx> {
|
||||
pub fn check_map_call(
|
||||
&mut self,
|
||||
cx: &LateContext<'tcx>,
|
||||
map_body: &'tcx Body<'tcx>,
|
||||
map_param_id: HirId,
|
||||
filter_param_id: HirId,
|
||||
is_filter_param_ref: bool,
|
||||
) -> Option<CheckResult<'tcx>> {
|
||||
match *self {
|
||||
OffendingFilterExpr::IsSome {
|
||||
receiver,
|
||||
side_effect_expr_span,
|
||||
}
|
||||
| OffendingFilterExpr::IsOk {
|
||||
receiver,
|
||||
side_effect_expr_span,
|
||||
} => {
|
||||
// check if closure ends with expect() or unwrap()
|
||||
if let ExprKind::MethodCall(seg, map_arg, ..) = map_body.value.kind
|
||||
&& matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or)
|
||||
// .map(|y| f(y).copied().unwrap())
|
||||
// ~~~~
|
||||
&& let map_arg_peeled = match map_arg.kind {
|
||||
ExprKind::MethodCall(method, original_arg, [], _) if acceptable_methods(method) => {
|
||||
original_arg
|
||||
},
|
||||
_ => map_arg,
|
||||
}
|
||||
// .map(|y| y[.acceptable_method()].unwrap())
|
||||
&& let simple_equal = (path_to_local_id(receiver, filter_param_id)
|
||||
&& path_to_local_id(map_arg_peeled, map_param_id))
|
||||
&& 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::Deref, expr_path) = a.kind;
|
||||
then { expr_path } else { a }
|
||||
};
|
||||
// let the filter closure arg and the map closure arg be equal
|
||||
path_to_local_id(a_path, filter_param_id)
|
||||
&& path_to_local_id(b, map_param_id)
|
||||
&& cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b)
|
||||
})
|
||||
&& (simple_equal
|
||||
|| SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(receiver, map_arg_peeled))
|
||||
{
|
||||
Some(CheckResult::Method {
|
||||
map_arg,
|
||||
side_effect_expr_span,
|
||||
method: match self {
|
||||
OffendingFilterExpr::IsSome { .. } => CalledMethod::OptionIsSome,
|
||||
OffendingFilterExpr::IsOk { .. } => CalledMethod::ResultIsOk,
|
||||
OffendingFilterExpr::Matches { .. } => unreachable!("only IsSome and IsOk can get here"),
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
OffendingFilterExpr::Matches { variant_def_id } => {
|
||||
let expr_uses_local = |pat: &Pat<'_>, expr: &Expr<'_>| {
|
||||
if let PatKind::TupleStruct(QPath::Resolved(_, path), [subpat], _) = pat.kind
|
||||
&& let PatKind::Binding(_, local_id, ident, _) = subpat.kind
|
||||
&& path_to_local_id(expr.peel_blocks(), local_id)
|
||||
&& let Some(local_variant_def_id) = path.res.opt_def_id()
|
||||
&& local_variant_def_id == variant_def_id
|
||||
{
|
||||
Some((ident, pat.span))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
// look for:
|
||||
// `if let Variant (v) = enum { v } else { unreachable!() }`
|
||||
// ^^^^^^^ ^ ^^^^ ^^^^^^^^^^^^^^^^^^
|
||||
// variant_span variant_ident scrutinee else_ (blocks peeled later)
|
||||
// OR
|
||||
// `match enum { Variant (v) => v, _ => unreachable!() }`
|
||||
// ^^^^ ^^^^^^^ ^ ^^^^^^^^^^^^^^
|
||||
// scrutinee variant_span variant_ident else_
|
||||
let (scrutinee, else_, variant_ident, variant_span) =
|
||||
match higher::IfLetOrMatch::parse(cx, map_body.value) {
|
||||
// For `if let` we want to check that the variant matching arm references the local created by its pattern
|
||||
Some(higher::IfLetOrMatch::IfLet(sc, pat, then, Some(else_)))
|
||||
if let Some((ident, span)) = expr_uses_local(pat, then) =>
|
||||
{
|
||||
(sc, else_, ident, span)
|
||||
},
|
||||
// For `match` we want to check that the "else" arm is the wildcard (`_`) pattern
|
||||
// and that the variant matching arm references the local created by its pattern
|
||||
Some(higher::IfLetOrMatch::Match(sc, [arm, wild_arm], MatchSource::Normal))
|
||||
if let PatKind::Wild = wild_arm.pat.kind
|
||||
&& let Some((ident, span)) = expr_uses_local(arm.pat, arm.body.peel_blocks()) =>
|
||||
{
|
||||
(sc, wild_arm.body, ident, span)
|
||||
},
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
if path_to_local_id(scrutinee, map_param_id)
|
||||
// else branch should be a `panic!` or `unreachable!` macro call
|
||||
&& let Some(mac) = root_macro_call(else_.peel_blocks().span)
|
||||
&& (is_panic(cx, mac.def_id) || cx.tcx.opt_item_name(mac.def_id) == Some(sym::unreachable))
|
||||
{
|
||||
Some(CheckResult::PatternMatching { variant_span, variant_ident })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn hir(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, filter_param_id: HirId) -> Option<Self> {
|
||||
if let ExprKind::MethodCall(path, receiver, [], _) = expr.kind
|
||||
&& let Some(recv_ty) = cx.typeck_results().expr_ty(receiver).peel_refs().ty_adt_def()
|
||||
{
|
||||
// we still want to lint if the expression possibly contains side effects,
|
||||
// *but* it can't be machine-applicable then, because that can change the behavior of the program:
|
||||
// .filter(|x| effect(x).is_some()).map(|x| effect(x).unwrap())
|
||||
// vs.
|
||||
// .filter_map(|x| effect(x))
|
||||
//
|
||||
// the latter only calls `effect` once
|
||||
let side_effect_expr_span = receiver.can_have_side_effects().then_some(receiver.span);
|
||||
|
||||
if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did())
|
||||
&& path.ident.name == sym!(is_some)
|
||||
{
|
||||
Some(Self::IsSome { receiver, side_effect_expr_span })
|
||||
} else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did())
|
||||
&& path.ident.name == sym!(is_ok)
|
||||
{
|
||||
Some(Self::IsOk { receiver, side_effect_expr_span })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else if let Some(macro_call) = root_macro_call(expr.span)
|
||||
&& cx.tcx.get_diagnostic_name(macro_call.def_id) == Some(sym::matches_macro)
|
||||
// we know for a fact that the wildcard pattern is the second arm
|
||||
&& let ExprKind::Match(scrutinee, [arm, _], _) = expr.kind
|
||||
&& path_to_local_id(scrutinee, filter_param_id)
|
||||
&& let PatKind::TupleStruct(QPath::Resolved(_, path), ..) = arm.pat.kind
|
||||
&& let Some(variant_def_id) = path.res.opt_def_id()
|
||||
{
|
||||
Some(OffendingFilterExpr::Matches { variant_def_id })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// is `filter(|x| x.is_some()).map(|x| x.unwrap())`
|
||||
fn is_filter_some_map_unwrap(
|
||||
cx: &LateContext<'_>,
|
||||
@ -102,55 +312,18 @@ pub(super) fn check(
|
||||
} 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).peel_refs().ty_adt_def();
|
||||
if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::Option, opt_ty.did()) {
|
||||
Some(false)
|
||||
} else if cx.tcx.is_diagnostic_item(sym::Result, 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 PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind;
|
||||
if let Some(mut offending_expr) = OffendingFilterExpr::hir(cx, filter_body.value, filter_param_id);
|
||||
|
||||
if let ExprKind::Closure(&Closure { body: 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);
|
||||
|
||||
// .filter(..).map(|y| f(y).copied().unwrap())
|
||||
// ~~~~
|
||||
let map_arg_peeled = match map_arg.kind {
|
||||
ExprKind::MethodCall(method, original_arg, [], _) if acceptable_methods(method) => {
|
||||
original_arg
|
||||
},
|
||||
_ => map_arg,
|
||||
};
|
||||
if let Some(check_result) =
|
||||
offending_expr.check_map_call(cx, map_body, map_param_id, filter_param_id, is_filter_param_ref);
|
||||
|
||||
// .filter(|x| x.is_some()).map(|y| y[.acceptable_method()].unwrap())
|
||||
let simple_equal = path_to_local_id(filter_arg, filter_param_id)
|
||||
&& path_to_local_id(map_arg_peeled, map_param_id);
|
||||
|
||||
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::Deref, expr_path) = a.kind;
|
||||
then { expr_path } else { a }
|
||||
};
|
||||
// let the filter closure arg and the map closure arg be equal
|
||||
path_to_local_id(a_path, filter_param_id)
|
||||
&& path_to_local_id(b, map_param_id)
|
||||
&& cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b)
|
||||
};
|
||||
|
||||
if simple_equal || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg_peeled);
|
||||
then {
|
||||
let span = filter_span.with_hi(expr.span.hi());
|
||||
let (filter_name, lint) = if is_find {
|
||||
@ -159,9 +332,12 @@ pub(super) fn check(
|
||||
("filter", MANUAL_FILTER_MAP)
|
||||
};
|
||||
let msg = format!("`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`");
|
||||
let (to_opt, deref) = if is_result {
|
||||
(".ok()", String::new())
|
||||
} else {
|
||||
|
||||
let (sugg, note_and_span, applicability) = match check_result {
|
||||
CheckResult::Method { map_arg, method, side_effect_expr_span } => {
|
||||
let (to_opt, deref) = match method {
|
||||
CalledMethod::ResultIsOk => (".ok()", String::new()),
|
||||
CalledMethod::OptionIsSome => {
|
||||
let derefs = cx.typeck_results()
|
||||
.expr_adjustments(map_arg)
|
||||
.iter()
|
||||
@ -169,12 +345,40 @@ pub(super) fn check(
|
||||
.count();
|
||||
|
||||
("", "*".repeat(derefs))
|
||||
}
|
||||
};
|
||||
|
||||
let sugg = format!(
|
||||
"{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})",
|
||||
snippet(cx, map_arg.span, ".."),
|
||||
);
|
||||
span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable);
|
||||
let (note_and_span, applicability) = if let Some(span) = side_effect_expr_span {
|
||||
let note = "the suggestion might change the behavior of the program when merging `filter` and `map`, \
|
||||
because this expression potentially contains side effects and will only execute once";
|
||||
|
||||
(Some((note, span)), Applicability::MaybeIncorrect)
|
||||
} else {
|
||||
(None, Applicability::MachineApplicable)
|
||||
};
|
||||
|
||||
(sugg, note_and_span, applicability)
|
||||
}
|
||||
CheckResult::PatternMatching { variant_span, variant_ident } => {
|
||||
let pat = snippet(cx, variant_span, "<pattern>");
|
||||
|
||||
(format!("{filter_name}_map(|{map_param_ident}| match {map_param_ident} {{ \
|
||||
{pat} => Some({variant_ident}), \
|
||||
_ => None \
|
||||
}})"), None, Applicability::MachineApplicable)
|
||||
}
|
||||
};
|
||||
span_lint_and_then(cx, lint, span, &msg, |diag| {
|
||||
diag.span_suggestion(span, "try", sugg, applicability);
|
||||
|
||||
if let Some((note, span)) = note_and_span {
|
||||
diag.span_note(span, note);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -138,6 +138,7 @@ pub fn hir(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> {
|
||||
}
|
||||
|
||||
/// An `if let` or `match` expression. Useful for lints that trigger on one or the other.
|
||||
#[derive(Debug)]
|
||||
pub enum IfLetOrMatch<'hir> {
|
||||
/// Any `match` expression
|
||||
Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource),
|
||||
|
@ -120,3 +120,27 @@ fn issue_8920() {
|
||||
.iter()
|
||||
.filter_map(|f| f.result_field.to_owned().ok());
|
||||
}
|
||||
|
||||
fn issue8010() {
|
||||
#[derive(Clone)]
|
||||
enum Enum {
|
||||
A(i32),
|
||||
B,
|
||||
}
|
||||
|
||||
let iter = [Enum::A(123), Enum::B].into_iter();
|
||||
|
||||
let _x = iter.clone().filter_map(|x| match x { Enum::A(s) => Some(s), _ => None });
|
||||
let _x = iter.clone().filter(|x| matches!(x, Enum::B)).map(|x| match x {
|
||||
Enum::A(s) => s,
|
||||
_ => unreachable!(),
|
||||
});
|
||||
let _x = iter
|
||||
.clone()
|
||||
.filter_map(|x| match x { Enum::A(s) => Some(s), _ => None });
|
||||
#[allow(clippy::unused_unit)]
|
||||
let _x = iter
|
||||
.clone()
|
||||
.filter(|x| matches!(x, Enum::B))
|
||||
.map(|x| if let Enum::B = x { () } else { unreachable!() });
|
||||
}
|
||||
|
@ -133,3 +133,31 @@ fn issue_8920() {
|
||||
.filter(|f| f.result_field.is_ok())
|
||||
.map(|f| f.result_field.to_owned().unwrap());
|
||||
}
|
||||
|
||||
fn issue8010() {
|
||||
#[derive(Clone)]
|
||||
enum Enum {
|
||||
A(i32),
|
||||
B,
|
||||
}
|
||||
|
||||
let iter = [Enum::A(123), Enum::B].into_iter();
|
||||
|
||||
let _x = iter.clone().filter(|x| matches!(x, Enum::A(_))).map(|x| match x {
|
||||
Enum::A(s) => s,
|
||||
_ => unreachable!(),
|
||||
});
|
||||
let _x = iter.clone().filter(|x| matches!(x, Enum::B)).map(|x| match x {
|
||||
Enum::A(s) => s,
|
||||
_ => unreachable!(),
|
||||
});
|
||||
let _x = iter
|
||||
.clone()
|
||||
.filter(|x| matches!(x, Enum::A(_)))
|
||||
.map(|x| if let Enum::A(s) = x { s } else { unreachable!() });
|
||||
#[allow(clippy::unused_unit)]
|
||||
let _x = iter
|
||||
.clone()
|
||||
.filter(|x| matches!(x, Enum::B))
|
||||
.map(|x| if let Enum::B = x { () } else { unreachable!() });
|
||||
}
|
||||
|
@ -4,6 +4,11 @@ error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
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: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_filter_map.rs:9:30
|
||||
|
|
||||
LL | let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
|
||||
| ^^^^^^^^^^
|
||||
= note: `-D clippy::manual-filter-map` implied by `-D warnings`
|
||||
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
@ -11,12 +16,24 @@ error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
|
|
||||
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))`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_filter_map.rs:12:31
|
||||
|
|
||||
LL | let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:15: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())`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_filter_map.rs:15:31
|
||||
|
|
||||
LL | let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:18:10
|
||||
@ -25,6 +42,12 @@ LL | .filter(|&x| to_ref(to_opt(x)).is_some())
|
||||
| __________^
|
||||
LL | | .map(|y| to_ref(to_opt(y)).unwrap());
|
||||
| |____________________________________________^ help: try: `filter_map(|y| *to_ref(to_opt(y)))`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_filter_map.rs:18:22
|
||||
|
|
||||
LL | .filter(|&x| to_ref(to_opt(x)).is_some())
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:21:10
|
||||
@ -33,6 +56,12 @@ LL | .filter(|x| to_ref(to_opt(*x)).is_some())
|
||||
| __________^
|
||||
LL | | .map(|y| to_ref(to_opt(y)).unwrap());
|
||||
| |____________________________________________^ help: try: `filter_map(|y| *to_ref(to_opt(y)))`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_filter_map.rs:21:21
|
||||
|
|
||||
LL | .filter(|x| to_ref(to_opt(*x)).is_some())
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:25:10
|
||||
@ -41,6 +70,12 @@ LL | .filter(|&x| to_ref(to_res(x)).is_ok())
|
||||
| __________^
|
||||
LL | | .map(|y| to_ref(to_res(y)).unwrap());
|
||||
| |____________________________________________^ help: try: `filter_map(|y| to_ref(to_res(y)).ok())`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_filter_map.rs:25:22
|
||||
|
|
||||
LL | .filter(|&x| to_ref(to_res(x)).is_ok())
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:28:10
|
||||
@ -49,6 +84,12 @@ LL | .filter(|x| to_ref(to_res(*x)).is_ok())
|
||||
| __________^
|
||||
LL | | .map(|y| to_ref(to_res(y)).unwrap());
|
||||
| |____________________________________________^ help: try: `filter_map(|y| to_ref(to_res(y)).ok())`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_filter_map.rs:28:21
|
||||
|
|
||||
LL | .filter(|x| to_ref(to_res(*x)).is_ok())
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:34:27
|
||||
@ -75,6 +116,12 @@ error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
|
|
||||
LL | iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned())`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_filter_map.rs:37:41
|
||||
|
|
||||
LL | iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:39:30
|
||||
@ -117,6 +164,12 @@ error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
|
|
||||
LL | iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned().ok())`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_filter_map.rs:45:45
|
||||
|
|
||||
LL | iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:93:10
|
||||
@ -190,5 +243,23 @@ LL | .filter(|f| f.result_field.is_ok())
|
||||
LL | | .map(|f| f.result_field.to_owned().unwrap());
|
||||
| |____________________________________________________^ help: try: `filter_map(|f| f.result_field.to_owned().ok())`
|
||||
|
||||
error: aborting due to 27 previous errors
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:146:27
|
||||
|
|
||||
LL | let _x = iter.clone().filter(|x| matches!(x, Enum::A(_))).map(|x| match x {
|
||||
| ___________________________^
|
||||
LL | | Enum::A(s) => s,
|
||||
LL | | _ => unreachable!(),
|
||||
LL | | });
|
||||
| |______^ help: try: `filter_map(|x| match x { Enum::A(s) => Some(s), _ => None })`
|
||||
|
||||
error: `filter(..).map(..)` can be simplified as `filter_map(..)`
|
||||
--> $DIR/manual_filter_map.rs:156:10
|
||||
|
|
||||
LL | .filter(|x| matches!(x, Enum::A(_)))
|
||||
| __________^
|
||||
LL | | .map(|x| if let Enum::A(s) = x { s } else { unreachable!() });
|
||||
| |_____________________________________________________________________^ help: try: `filter_map(|x| match x { Enum::A(s) => Some(s), _ => None })`
|
||||
|
||||
error: aborting due to 29 previous errors
|
||||
|
||||
|
@ -4,6 +4,11 @@ error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
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: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_find_map.rs:9:28
|
||||
|
|
||||
LL | let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap());
|
||||
| ^^^^^^^^^^
|
||||
= note: `-D clippy::manual-find-map` implied by `-D warnings`
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
@ -11,12 +16,24 @@ error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
|
|
||||
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))`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_find_map.rs:12:29
|
||||
|
|
||||
LL | let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi"));
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_find_map.rs:15: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())`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_find_map.rs:15:29
|
||||
|
|
||||
LL | let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_find_map.rs:18:10
|
||||
@ -25,6 +42,12 @@ LL | .find(|&x| to_ref(to_opt(x)).is_some())
|
||||
| __________^
|
||||
LL | | .map(|y| to_ref(to_opt(y)).unwrap());
|
||||
| |____________________________________________^ help: try: `find_map(|y| *to_ref(to_opt(y)))`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_find_map.rs:18:20
|
||||
|
|
||||
LL | .find(|&x| to_ref(to_opt(x)).is_some())
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_find_map.rs:21:10
|
||||
@ -33,6 +56,12 @@ LL | .find(|x| to_ref(to_opt(*x)).is_some())
|
||||
| __________^
|
||||
LL | | .map(|y| to_ref(to_opt(y)).unwrap());
|
||||
| |____________________________________________^ help: try: `find_map(|y| *to_ref(to_opt(y)))`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_find_map.rs:21:19
|
||||
|
|
||||
LL | .find(|x| to_ref(to_opt(*x)).is_some())
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_find_map.rs:25:10
|
||||
@ -41,6 +70,12 @@ LL | .find(|&x| to_ref(to_res(x)).is_ok())
|
||||
| __________^
|
||||
LL | | .map(|y| to_ref(to_res(y)).unwrap());
|
||||
| |____________________________________________^ help: try: `find_map(|y| to_ref(to_res(y)).ok())`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_find_map.rs:25:20
|
||||
|
|
||||
LL | .find(|&x| to_ref(to_res(x)).is_ok())
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_find_map.rs:28:10
|
||||
@ -49,6 +84,12 @@ LL | .find(|x| to_ref(to_res(*x)).is_ok())
|
||||
| __________^
|
||||
LL | | .map(|y| to_ref(to_res(y)).unwrap());
|
||||
| |____________________________________________^ help: try: `find_map(|y| to_ref(to_res(y)).ok())`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_find_map.rs:28:19
|
||||
|
|
||||
LL | .find(|x| to_ref(to_res(*x)).is_ok())
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_find_map.rs:34:26
|
||||
@ -91,6 +132,12 @@ error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
|
|
||||
LL | iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned())`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_find_map.rs:40:41
|
||||
|
|
||||
LL | iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_find_map.rs:42:30
|
||||
@ -133,6 +180,12 @@ error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
|
|
||||
LL | iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned().ok())`
|
||||
|
|
||||
note: the suggestion might change the behavior of the program when merging `filter` and `map`, because this expression potentially contains side effects and will only execute once
|
||||
--> $DIR/manual_find_map.rs:48:45
|
||||
|
|
||||
LL | iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `find(..).map(..)` can be simplified as `find_map(..)`
|
||||
--> $DIR/manual_find_map.rs:96:10
|
||||
|
Loading…
Reference in New Issue
Block a user