1
1
use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
+ use clippy_utils:: eager_or_lazy:: switch_to_eager_eval;
2
3
use clippy_utils:: path_to_local_id;
3
4
use clippy_utils:: source:: snippet;
4
5
use clippy_utils:: ty:: is_type_diagnostic_item;
@@ -19,35 +20,47 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def:
19
20
|| is_type_diagnostic_item ( cx, recv_ty, sym:: Result ) )
20
21
// check we are dealing with boolean map_or
21
22
&& let Bool ( def_bool) = def_kind. node
22
- && def_bool == false
23
23
&& let ExprKind :: Closure ( map_closure) = map. kind
24
24
&& let closure_body = cx. tcx . hir ( ) . body ( map_closure. body )
25
25
&& let closure_body_value = closure_body. value . peel_blocks ( )
26
- // check we are dealing with equality statement in the closure
26
+ // switching from closure to direct comparison changes the comparison
27
+ // from lazy (when map_or evaluates to first param) to eager, so we force eager here
28
+ // this stops the lint running whenever the closure has a potentially expensive
29
+ // computation inside of it
30
+ && switch_to_eager_eval ( cx, closure_body_value)
27
31
&& let ExprKind :: Binary ( op, l, r) = closure_body_value. kind
28
32
&& let Some ( param) = closure_body. params . first ( )
29
33
&& let PatKind :: Binding ( _, hir_id, _, _) = param. pat . kind
30
- && BinOpKind :: Eq == op. node
31
- && ( path_to_local_id ( l, hir_id) || path_to_local_id ( r, hir_id) )
34
+ // checking that map_or is either:
35
+ // .map_or(false, |x| x == y) OR .map_or(true, |x| x != y)
36
+ && ( ( BinOpKind :: Eq == op. node && !def_bool) || ( BinOpKind :: Ne == op. node && def_bool) )
37
+ // check that either the left or right side of the comparison
38
+ // is the binding param to the closure
39
+ // xor, because if its both then thats a strange edge case and
40
+ // we can just ignore it, since by default clippy will error on this
41
+ && ( path_to_local_id ( l, hir_id) ^ path_to_local_id ( r, hir_id) )
32
42
{
33
43
let wrap = if is_type_diagnostic_item ( cx, recv_ty, sym:: Option ) {
34
44
"Some"
35
45
} else {
36
46
"Result"
37
47
} ;
38
48
49
+ let comparator = if BinOpKind :: Eq == op. node { "==" } else { "!=" } ;
50
+
39
51
// we already checked either l or r is the local id earlier
40
52
let non_binding_location = if path_to_local_id ( l, hir_id) { r } else { l } ;
41
53
42
54
span_lint_and_sugg (
43
55
cx,
44
56
UNNECESSARY_PATTERN_MATCHING ,
45
57
expr. span ,
46
- "`map_or` here is redundant, use typical equality instead" ,
58
+ "`map_or` here is redundant, use standard comparison instead" ,
47
59
"try" ,
48
60
format ! (
49
- "{} == {}({})" ,
61
+ "{} {} {}({})" ,
50
62
snippet( cx, recv. span, ".." ) ,
63
+ comparator,
51
64
wrap,
52
65
snippet( cx, non_binding_location. span. source_callsite( ) , ".." )
53
66
) ,
0 commit comments