1
1
use clippy_utils:: diagnostics:: span_lint_and_then;
2
2
use clippy_utils:: msrvs:: { self , Msrv } ;
3
3
use clippy_utils:: source:: snippet_with_applicability;
4
- use clippy_utils:: ty:: { is_copy , is_type_diagnostic_item } ;
4
+ use clippy_utils:: ty:: { get_type_diagnostic_name , is_copy } ;
5
5
use rustc_data_structures:: fx:: FxHashSet ;
6
6
use rustc_errors:: Applicability ;
7
7
use rustc_hir:: def:: Res ;
@@ -27,116 +27,120 @@ pub(super) fn check<'tcx>(
27
27
msrv : Msrv ,
28
28
) {
29
29
let recv_ty = cx. typeck_results ( ) . expr_ty ( recv) . peel_refs ( ) ;
30
- let is_option = is_type_diagnostic_item ( cx, recv_ty, sym:: Option ) ;
31
- let is_result = is_type_diagnostic_item ( cx, recv_ty, sym:: Result ) ;
30
+ let is_option = match get_type_diagnostic_name ( cx, recv_ty) {
31
+ Some ( sym:: Option ) => true ,
32
+ Some ( sym:: Result ) if msrv. meets ( cx, msrvs:: RESULT_MAP_OR ) => false ,
33
+ _ => return ,
34
+ } ;
35
+
36
+ let unwrap_arg_ty = cx. typeck_results ( ) . expr_ty ( unwrap_arg) ;
37
+ if !is_copy ( cx, unwrap_arg_ty) {
38
+ // Replacing `.map(<f>).unwrap_or(<a>)` with `.map_or(<a>, <f>)` can sometimes lead to
39
+ // borrowck errors, see #10579 for one such instance.
40
+ // In particular, if `a` causes a move and `f` references that moved binding, then we cannot lint:
41
+ // ```
42
+ // let x = vec![1, 2];
43
+ // x.get(0..1).map(|s| s.to_vec()).unwrap_or(x);
44
+ // ```
45
+ // This compiles, but changing it to `map_or` will produce a compile error:
46
+ // ```
47
+ // let x = vec![1, 2];
48
+ // x.get(0..1).map_or(x, |s| s.to_vec())
49
+ // ^ moving `x` here
50
+ // ^^^^^^^^^^^ while it is borrowed here (and later used in the closure)
51
+ // ```
52
+ // So, we have to check that `a` is not referenced anywhere (even outside of the `.map` closure!)
53
+ // before the call to `unwrap_or`.
54
+
55
+ let mut unwrap_visitor = UnwrapVisitor {
56
+ cx,
57
+ identifiers : FxHashSet :: default ( ) ,
58
+ } ;
59
+ unwrap_visitor. visit_expr ( unwrap_arg) ;
32
60
33
- if is_result && !msrv. meets ( cx, msrvs:: RESULT_MAP_OR ) {
34
- return ;
35
- }
61
+ let mut reference_visitor = ReferenceVisitor {
62
+ cx,
63
+ identifiers : unwrap_visitor. identifiers ,
64
+ unwrap_or_span : unwrap_arg. span ,
65
+ } ;
36
66
37
- // lint if the caller of `map()` is an `Option`
38
- if is_option || is_result {
39
- if !is_copy ( cx, cx. typeck_results ( ) . expr_ty ( unwrap_arg) ) {
40
- // Replacing `.map(<f>).unwrap_or(<a>)` with `.map_or(<a>, <f>)` can sometimes lead to
41
- // borrowck errors, see #10579 for one such instance.
42
- // In particular, if `a` causes a move and `f` references that moved binding, then we cannot lint:
43
- // ```
44
- // let x = vec![1, 2];
45
- // x.get(0..1).map(|s| s.to_vec()).unwrap_or(x);
46
- // ```
47
- // This compiles, but changing it to `map_or` will produce a compile error:
48
- // ```
49
- // let x = vec![1, 2];
50
- // x.get(0..1).map_or(x, |s| s.to_vec())
51
- // ^ moving `x` here
52
- // ^^^^^^^^^^^ while it is borrowed here (and later used in the closure)
53
- // ```
54
- // So, we have to check that `a` is not referenced anywhere (even outside of the `.map` closure!)
55
- // before the call to `unwrap_or`.
56
-
57
- let mut unwrap_visitor = UnwrapVisitor {
58
- cx,
59
- identifiers : FxHashSet :: default ( ) ,
60
- } ;
61
- unwrap_visitor. visit_expr ( unwrap_arg) ;
62
-
63
- let mut reference_visitor = ReferenceVisitor {
64
- cx,
65
- identifiers : unwrap_visitor. identifiers ,
66
- unwrap_or_span : unwrap_arg. span ,
67
- } ;
68
-
69
- let body = cx. tcx . hir_body_owned_by ( cx. tcx . hir_enclosing_body_owner ( expr. hir_id ) ) ;
70
-
71
- // Visit the body, and return if we've found a reference
72
- if reference_visitor. visit_body ( body) . is_break ( ) {
73
- return ;
74
- }
75
- }
67
+ let body = cx. tcx . hir_body_owned_by ( cx. tcx . hir_enclosing_body_owner ( expr. hir_id ) ) ;
76
68
77
- if !unwrap_arg. span . eq_ctxt ( map_span) {
69
+ // Visit the body, and return if we've found a reference
70
+ if reference_visitor. visit_body ( body) . is_break ( ) {
78
71
return ;
79
72
}
73
+ }
74
+
75
+ if !unwrap_arg. span . eq_ctxt ( map_span) {
76
+ return ;
77
+ }
80
78
81
- // is_some_and is stabilised && `unwrap_or` argument is false; suggest `is_some_and` instead
82
- let suggest_is_some_and = matches ! ( & unwrap_arg. kind, ExprKind :: Lit ( lit)
79
+ // is_some_and is stabilised && `unwrap_or` argument is false; suggest `is_some_and` instead
80
+ let suggest_is_some_and = matches ! ( & unwrap_arg. kind, ExprKind :: Lit ( lit)
83
81
if matches!( lit. node, rustc_ast:: LitKind :: Bool ( false ) ) )
84
- && msrv. meets ( cx, msrvs:: OPTION_RESULT_IS_VARIANT_AND ) ;
85
-
86
- let mut applicability = Applicability :: MachineApplicable ;
87
- // get snippet for unwrap_or()
88
- let unwrap_snippet = snippet_with_applicability ( cx, unwrap_arg. span , ".." , & mut applicability) ;
89
- // lint message
90
- // comparing the snippet from source to raw text ("None") below is safe
91
- // because we already have checked the type.
92
- let unwrap_snippet_none = is_option && unwrap_snippet == "None" ;
93
- let arg = if unwrap_snippet_none {
94
- "None"
95
- } else if suggest_is_some_and {
96
- "false"
97
- } else {
98
- "<a>"
99
- } ;
100
- let suggest = if unwrap_snippet_none {
101
- "and_then(<f>)"
102
- } else if suggest_is_some_and {
103
- if is_result {
104
- "is_ok_and(<f>)"
105
- } else {
106
- "is_some_and(<f>)"
107
- }
82
+ && msrv. meets ( cx, msrvs:: OPTION_RESULT_IS_VARIANT_AND ) ;
83
+
84
+ let mut applicability = Applicability :: MachineApplicable ;
85
+ // get snippet for unwrap_or()
86
+ let unwrap_snippet = snippet_with_applicability ( cx, unwrap_arg. span , ".." , & mut applicability) ;
87
+ // lint message
88
+ // comparing the snippet from source to raw text ("None") below is safe
89
+ // because we already have checked the type.
90
+ let unwrap_snippet_none = is_option && unwrap_snippet == "None" ;
91
+ let arg = if unwrap_snippet_none {
92
+ "None"
93
+ } else if suggest_is_some_and {
94
+ "false"
95
+ } else {
96
+ "<a>"
97
+ } ;
98
+ let suggest = if unwrap_snippet_none {
99
+ "and_then(<f>)"
100
+ } else if suggest_is_some_and {
101
+ if is_option {
102
+ "is_some_and(<f>)"
108
103
} else {
109
- "map_or(<a>, <f>)"
110
- } ;
111
- let msg = format ! (
112
- "called `map(<f>).unwrap_or({arg})` on an `{}` value" ,
113
- if is_option { "Option" } else { "Result" }
114
- ) ;
115
-
116
- span_lint_and_then ( cx, MAP_UNWRAP_OR , expr. span , msg, |diag| {
117
- let map_arg_span = map_arg. span ;
118
-
119
- let mut suggestion = vec ! [
120
- (
121
- map_span,
122
- String :: from( if unwrap_snippet_none {
123
- "and_then"
124
- } else if suggest_is_some_and {
125
- if is_result { "is_ok_and" } else { "is_some_and" }
126
- } else {
127
- "map_or"
128
- } ) ,
129
- ) ,
130
- ( expr. span. with_lo( unwrap_recv. span. hi( ) ) , String :: new( ) ) ,
131
- ] ;
132
-
133
- if !unwrap_snippet_none && !suggest_is_some_and {
134
- suggestion. push ( ( map_arg_span. with_hi ( map_arg_span. lo ( ) ) , format ! ( "{unwrap_snippet}, " ) ) ) ;
135
- }
136
-
137
- diag. multipart_suggestion ( format ! ( "use `{suggest}` instead" ) , suggestion, applicability) ;
138
- } ) ;
139
- }
104
+ "is_ok_and(<f>)"
105
+ }
106
+ } else {
107
+ "map_or(<a>, <f>)"
108
+ } ;
109
+ let msg = format ! (
110
+ "called `map(<f>).unwrap_or({arg})` on an `{}` value" ,
111
+ if is_option { "Option" } else { "Result" }
112
+ ) ;
113
+
114
+ span_lint_and_then ( cx, MAP_UNWRAP_OR , expr. span , msg, |diag| {
115
+ let map_arg_span = map_arg. span ;
116
+
117
+ let mut suggestion = vec ! [
118
+ (
119
+ map_span,
120
+ String :: from( if unwrap_snippet_none {
121
+ "and_then"
122
+ } else if suggest_is_some_and {
123
+ if is_option { "is_some_and" } else { "is_ok_and" }
124
+ } else {
125
+ let unwrap_arg_ty = unwrap_arg_ty. peel_refs( ) ;
126
+ if unwrap_arg_ty. is_array( )
127
+ && let unwrap_arg_ty_adj = cx. typeck_results( ) . expr_ty_adjusted( unwrap_arg) . peel_refs( )
128
+ && unwrap_arg_ty_adj. is_slice( )
129
+ {
130
+ return ;
131
+ }
132
+ "map_or"
133
+ } ) ,
134
+ ) ,
135
+ ( expr. span. with_lo( unwrap_recv. span. hi( ) ) , String :: new( ) ) ,
136
+ ] ;
137
+
138
+ if !unwrap_snippet_none && !suggest_is_some_and {
139
+ suggestion. push ( ( map_arg_span. with_hi ( map_arg_span. lo ( ) ) , format ! ( "{unwrap_snippet}, " ) ) ) ;
140
+ }
141
+
142
+ diag. multipart_suggestion ( format ! ( "use `{suggest}` instead" ) , suggestion, applicability) ;
143
+ } ) ;
140
144
}
141
145
142
146
struct UnwrapVisitor < ' a , ' tcx > {
0 commit comments