@@ -4,7 +4,7 @@ use clippy_utils::eager_or_lazy::switch_to_eager_eval;
4
4
use clippy_utils:: source:: { snippet, snippet_opt} ;
5
5
use clippy_utils:: ty:: is_type_diagnostic_item;
6
6
use clippy_utils:: visitors:: is_local_used;
7
- use clippy_utils:: { get_parent_expr, path_to_local_id} ;
7
+ use clippy_utils:: { get_parent_expr, is_from_proc_macro , path_to_local_id} ;
8
8
use rustc_ast:: util:: parser:: AssocOp ;
9
9
use rustc_ast:: LitKind :: Bool ;
10
10
use rustc_errors:: Applicability ;
@@ -34,86 +34,99 @@ impl Variant {
34
34
}
35
35
}
36
36
37
- // Only checking map_or for now
38
- pub ( super ) fn check (
39
- cx : & LateContext < ' _ > ,
40
- expr : & Expr < ' _ > ,
37
+ pub ( super ) fn check < ' a > (
38
+ cx : & LateContext < ' a > ,
39
+ expr : & Expr < ' a > ,
41
40
recv : & Expr < ' _ > ,
42
41
def : & Expr < ' _ > ,
43
42
map : & Expr < ' _ > ,
44
43
msrv : & Msrv ,
45
44
) {
46
- if let ExprKind :: Lit ( def_kind) = def. kind
47
- && let typeck_results = cx. typeck_results ( )
48
- && let recv_ty = typeck_results. expr_ty ( recv)
49
- && ( is_type_diagnostic_item ( cx, recv_ty, sym:: Option ) || is_type_diagnostic_item ( cx, recv_ty, sym:: Result ) )
50
- && let Bool ( def_bool) = def_kind. node
51
- {
52
- let variant = if is_type_diagnostic_item ( cx, recv_ty, sym:: Option ) {
53
- Variant :: Some
54
- } else {
55
- Variant :: Ok
56
- } ;
57
-
58
- let ( sugg, method) = if let ExprKind :: Closure ( map_closure) = map. kind
59
- && let closure_body = cx. tcx . hir ( ) . body ( map_closure. body )
60
- && let closure_body_value = closure_body. value . peel_blocks ( )
61
- && let ExprKind :: Binary ( op, l, r) = closure_body_value. kind
62
- && let Some ( param) = closure_body. params . first ( )
63
- && let PatKind :: Binding ( _, hir_id, _, _) = param. pat . kind
64
- // checking that map_or is one of the following:
65
- // .map_or(false, |x| x == y)
66
- // .map_or(false, |x| y == x) - swapped comparison
67
- // .map_or(true, |x| x != y)
68
- // .map_or(true, |x| y != x) - swapped comparison
69
- && ( ( BinOpKind :: Eq == op. node && !def_bool) || ( BinOpKind :: Ne == op. node && def_bool) )
70
- && let non_binding_location = if path_to_local_id ( l, hir_id) { r } else { l }
71
- && switch_to_eager_eval ( cx, non_binding_location)
72
- // xor, because if its both then thats a strange edge case and
73
- // we can just ignore it, since by default clippy will error on this
74
- && ( path_to_local_id ( l, hir_id) ^ path_to_local_id ( r, hir_id) )
75
- && !is_local_used ( cx, non_binding_location, hir_id)
76
- && typeck_results. expr_ty ( l) == typeck_results. expr_ty ( r)
77
- {
78
- let wrap = variant. variant_name ( ) ;
79
- let comparator = op. node . as_str ( ) ;
45
+ if is_from_proc_macro ( cx, expr) {
46
+ return ;
47
+ }
80
48
81
- // we may need to add parens around the suggestion
82
- // in case the parent expression has additional method calls,
83
- // since for example `Some(5).map_or(false, |x| x == 5).then(|| 1)`
84
- // being converted to `Some(5) == Some(5).then(|| 1)` isnt
85
- // the same thing
86
- let should_add_parens = get_parent_expr ( cx, expr)
87
- . is_some_and ( |expr| expr. precedence ( ) . order ( ) > i8:: try_from ( AssocOp :: Equal . precedence ( ) ) . unwrap_or ( 0 ) ) ;
88
- (
89
- format ! (
90
- "{}{} {comparator} {wrap}({}){}" ,
91
- if should_add_parens { "(" } else { "" } ,
92
- snippet( cx, recv. span, ".." ) ,
93
- snippet( cx, non_binding_location. span. source_callsite( ) , ".." ) ,
94
- if should_add_parens { ")" } else { "" }
95
- ) ,
96
- "standard comparison" ,
97
- )
98
- } else if !def_bool
99
- && msrv. meets ( msrvs:: OPTION_RESULT_IS_VARIANT_AND )
100
- && let Some ( recv_callsite) = snippet_opt ( cx, recv. span . source_callsite ( ) )
101
- && let Some ( span_callsite) = snippet_opt ( cx, map. span . source_callsite ( ) )
102
- {
103
- let sugg = variant. method_name ( ) ;
104
- ( format ! ( "{recv_callsite}.{sugg}({span_callsite})" , ) , sugg)
105
- } else {
106
- return ;
107
- } ;
49
+ let ExprKind :: Lit ( def_kind) = def. kind else {
50
+ return ;
51
+ } ;
108
52
109
- span_lint_and_sugg (
110
- cx,
111
- UNNECESSARY_MAP_OR ,
112
- expr. span ,
113
- format ! ( "`map_or` is redundant, use {method} instead" ) ,
114
- "try" ,
115
- sugg,
116
- Applicability :: MachineApplicable ,
117
- ) ;
53
+ let recv_ty = cx. typeck_results ( ) . expr_ty ( recv) ;
54
+ if !( is_type_diagnostic_item ( cx, recv_ty, sym:: Option ) || is_type_diagnostic_item ( cx, recv_ty, sym:: Result ) ) {
55
+ return ;
118
56
}
57
+
58
+ let Bool ( def_bool) = def_kind. node else {
59
+ return ;
60
+ } ;
61
+
62
+ let variant = if is_type_diagnostic_item ( cx, recv_ty, sym:: Option ) {
63
+ Variant :: Some
64
+ } else {
65
+ Variant :: Ok
66
+ } ;
67
+
68
+ let ( sugg, method) = if let ExprKind :: Closure ( map_closure) = map. kind
69
+ && let closure_body = cx. tcx . hir ( ) . body ( map_closure. body )
70
+ && let closure_body_value = closure_body. value . peel_blocks ( )
71
+ && let ExprKind :: Binary ( op, l, r) = closure_body_value. kind
72
+ && let Some ( param) = closure_body. params . first ( )
73
+ && let PatKind :: Binding ( _, hir_id, _, _) = param. pat . kind
74
+ // checking that map_or is one of the following:
75
+ // .map_or(false, |x| x == y)
76
+ // .map_or(false, |x| y == x) - swapped comparison
77
+ // .map_or(true, |x| x != y)
78
+ // .map_or(true, |x| y != x) - swapped comparison
79
+ && ( ( BinOpKind :: Eq == op. node && !def_bool) || ( BinOpKind :: Ne == op. node && def_bool) )
80
+ && let non_binding_location = if path_to_local_id ( l, hir_id) { r } else { l }
81
+ && switch_to_eager_eval ( cx, non_binding_location)
82
+ // xor, because if its both then thats a strange edge case and
83
+ // we can just ignore it, since by default clippy will error on this
84
+ && ( path_to_local_id ( l, hir_id) ^ path_to_local_id ( r, hir_id) )
85
+ && !is_local_used ( cx, non_binding_location, hir_id)
86
+ && let typeck_results = cx. typeck_results ( )
87
+ && typeck_results. expr_ty ( l) == typeck_results. expr_ty ( r)
88
+ {
89
+ let wrap = variant. variant_name ( ) ;
90
+ let comparator = op. node . as_str ( ) ;
91
+
92
+ // we may need to add parens around the suggestion
93
+ // in case the parent expression has additional method calls,
94
+ // since for example `Some(5).map_or(false, |x| x == 5).then(|| 1)`
95
+ // being converted to `Some(5) == Some(5).then(|| 1)` isnt
96
+ // the same thing
97
+ let should_add_parens = get_parent_expr ( cx, expr)
98
+ . is_some_and ( |expr| expr. precedence ( ) . order ( ) > i8:: try_from ( AssocOp :: Equal . precedence ( ) ) . unwrap_or ( 0 ) ) ;
99
+ (
100
+ format ! (
101
+ "{}{} {comparator} {wrap}({}){}" ,
102
+ if should_add_parens { "(" } else { "" } ,
103
+ snippet( cx, recv. span, ".." ) ,
104
+ snippet( cx, non_binding_location. span. source_callsite( ) , ".." ) ,
105
+ if should_add_parens { ")" } else { "" }
106
+ ) ,
107
+ "standard comparison" ,
108
+ )
109
+ } else if !def_bool
110
+ && msrv. meets ( msrvs:: OPTION_RESULT_IS_VARIANT_AND )
111
+ && let Some ( recv_callsite) = snippet_opt ( cx, recv. span . source_callsite ( ) )
112
+ && let Some ( span_callsite) = snippet_opt ( cx, map. span . source_callsite ( ) )
113
+ {
114
+ let suggested_name = variant. method_name ( ) ;
115
+ (
116
+ format ! ( "{recv_callsite}.{suggested_name}({span_callsite})" , ) ,
117
+ suggested_name,
118
+ )
119
+ } else {
120
+ return ;
121
+ } ;
122
+
123
+ span_lint_and_sugg (
124
+ cx,
125
+ UNNECESSARY_MAP_OR ,
126
+ expr. span ,
127
+ format ! ( "this `map_or` is redundant" ) ,
128
+ format ! ( "use {method} instead" ) ,
129
+ sugg,
130
+ Applicability :: MachineApplicable ,
131
+ ) ;
119
132
}
0 commit comments