@@ -4,7 +4,7 @@ use clippy_utils::eager_or_lazy::switch_to_eager_eval;
44use clippy_utils:: source:: { snippet, snippet_opt} ;
55use clippy_utils:: ty:: is_type_diagnostic_item;
66use 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} ;
88use rustc_ast:: util:: parser:: AssocOp ;
99use rustc_ast:: LitKind :: Bool ;
1010use rustc_errors:: Applicability ;
@@ -34,86 +34,99 @@ impl Variant {
3434 }
3535}
3636
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 > ,
4140 recv : & Expr < ' _ > ,
4241 def : & Expr < ' _ > ,
4342 map : & Expr < ' _ > ,
4443 msrv : & Msrv ,
4544) {
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+ }
8048
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+ } ;
10852
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 ;
11856 }
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+ ) ;
119132}
0 commit comments