1
- use clippy_utils:: diagnostics:: { span_lint_and_help, span_lint_and_sugg, span_lint_and_then } ;
1
+ use clippy_utils:: diagnostics:: { span_lint_and_help, span_lint_and_sugg} ;
2
2
use clippy_utils:: source:: { snippet, snippet_with_applicability} ;
3
3
use clippy_utils:: ty:: is_type_diagnostic_item;
4
- use clippy_utils:: { is_trait_method, strip_pat_refs} ;
4
+ use clippy_utils:: { get_parent_expr_for_hir , is_trait_method, strip_pat_refs} ;
5
5
use if_chain:: if_chain;
6
6
use rustc_errors:: Applicability ;
7
7
use rustc_hir as hir;
8
- use rustc_hir:: { self , HirId , HirIdMap , HirIdSet , PatKind } ;
8
+ use rustc_hir:: { self , Expr , ExprKind , HirId , PatKind } ;
9
9
use rustc_infer:: infer:: TyCtxtInferExt ;
10
10
use rustc_lint:: LateContext ;
11
11
use rustc_middle:: hir:: place:: ProjectionKind ;
12
- use rustc_middle:: mir:: FakeReadCause ;
12
+ use rustc_middle:: mir:: { FakeReadCause , Mutability } ;
13
13
use rustc_middle:: ty;
14
- use rustc_span:: source_map:: Span ;
14
+ use rustc_span:: source_map:: { BytePos , Span } ;
15
15
use rustc_span:: symbol:: sym;
16
16
use rustc_typeck:: expr_use_visitor:: { Delegate , ExprUseVisitor , PlaceBase , PlaceWithHirId } ;
17
17
@@ -46,42 +46,12 @@ pub(super) fn check<'tcx>(
46
46
if let hir:: ExprKind :: Closure ( _, _, body_id, ..) = search_arg. kind;
47
47
let closure_body = cx. tcx. hir( ) . body( body_id) ;
48
48
if let Some ( closure_arg) = closure_body. params. get( 0 ) ;
49
-
50
49
then {
51
50
if let hir:: PatKind :: Ref ( ..) = closure_arg. pat. kind {
52
- Some ( ( search_snippet. replacen( '&' , "" , 1 ) , None ) )
51
+ Some ( search_snippet. replacen( '&' , "" , 1 ) )
53
52
} else if let PatKind :: Binding ( ..) = strip_pat_refs( closure_arg. pat) . kind {
54
- let mut visitor = DerefDelegate {
55
- cx,
56
- set: HirIdSet :: default ( ) ,
57
- deref_suggs: HirIdMap :: default ( ) ,
58
- borrow_suggs: HirIdMap :: default ( )
59
- } ;
60
-
61
- let fn_def_id = cx. tcx. hir( ) . local_def_id( search_arg. hir_id) ;
62
- cx. tcx. infer_ctxt( ) . enter( |infcx| {
63
- ExprUseVisitor :: new(
64
- & mut visitor, & infcx, fn_def_id, cx. param_env, cx. typeck_results( )
65
- ) . consume_body( closure_body) ;
66
- } ) ;
67
-
68
- let replacements = if visitor. set. is_empty( ) {
69
- None
70
- } else {
71
- let mut deref_suggs = Vec :: new( ) ;
72
- let mut borrow_suggs = Vec :: new( ) ;
73
- for node in visitor. set {
74
- let span = cx. tcx. hir( ) . span( node) ;
75
- if let Some ( sugg) = visitor. deref_suggs. get( & node) {
76
- deref_suggs. push( ( span, sugg. clone( ) ) ) ;
77
- }
78
- if let Some ( sugg) = visitor. borrow_suggs. get( & node) {
79
- borrow_suggs. push( ( span, sugg. clone( ) ) ) ;
80
- }
81
- }
82
- Some ( ( deref_suggs, borrow_suggs) )
83
- } ;
84
- Some ( ( search_snippet. to_string( ) , replacements) )
53
+ get_closure_suggestion( cx, search_arg, closure_body)
54
+ . or_else( || Some ( search_snippet. to_string( ) ) )
85
55
} else {
86
56
None
87
57
}
@@ -90,38 +60,35 @@ pub(super) fn check<'tcx>(
90
60
}
91
61
} ;
92
62
// add note if not multi-line
93
- let ( closure_snippet, replacements) = any_search_snippet
94
- . as_ref ( )
95
- . map_or ( ( & * search_snippet, None ) , |s| ( & s. 0 , s. 1 . clone ( ) ) ) ;
96
- let ( span, help, sugg) = if is_some {
97
- (
63
+ if is_some {
64
+ span_lint_and_sugg (
65
+ cx,
66
+ SEARCH_IS_SOME ,
98
67
method_span. with_hi ( expr. span . hi ( ) ) ,
68
+ & msg,
99
69
"use `any()` instead" ,
100
- format ! ( "any({})" , closure_snippet) ,
101
- )
70
+ format ! (
71
+ "any({})" ,
72
+ any_search_snippet. as_ref( ) . map_or( & * search_snippet, String :: as_str)
73
+ ) ,
74
+ Applicability :: MachineApplicable ,
75
+ ) ;
102
76
} else {
103
77
let iter = snippet ( cx, search_recv. span , ".." ) ;
104
- (
78
+ span_lint_and_sugg (
79
+ cx,
80
+ SEARCH_IS_SOME ,
105
81
expr. span ,
82
+ & msg,
106
83
"use `!_.any()` instead" ,
107
- format ! ( "!{}.any({})" , iter, closure_snippet) ,
108
- )
109
- } ;
110
-
111
- span_lint_and_then ( cx, SEARCH_IS_SOME , span, & msg, |db| {
112
- if let Some ( ( deref_suggs, borrow_suggs) ) = replacements {
113
- db. span_suggestion ( span, help, sugg, Applicability :: MaybeIncorrect ) ;
114
-
115
- if !deref_suggs. is_empty ( ) {
116
- db. multipart_suggestion ( "...and remove deref" , deref_suggs, Applicability :: MaybeIncorrect ) ;
117
- }
118
- if !borrow_suggs. is_empty ( ) {
119
- db. multipart_suggestion ( "...and borrow variable" , borrow_suggs, Applicability :: MaybeIncorrect ) ;
120
- }
121
- } else {
122
- db. span_suggestion ( span, help, sugg, Applicability :: MachineApplicable ) ;
123
- }
124
- } ) ;
84
+ format ! (
85
+ "!{}.any({})" ,
86
+ iter,
87
+ any_search_snippet. as_ref( ) . map_or( & * search_snippet, String :: as_str)
88
+ ) ,
89
+ Applicability :: MachineApplicable ,
90
+ ) ;
91
+ }
125
92
} else {
126
93
let hint = format ! (
127
94
"this is more succinctly expressed by calling `any()`{}" ,
@@ -184,53 +151,86 @@ pub(super) fn check<'tcx>(
184
151
}
185
152
}
186
153
154
+ fn get_closure_suggestion < ' tcx > (
155
+ cx : & LateContext < ' _ > ,
156
+ search_arg : & ' tcx hir:: Expr < ' _ > ,
157
+ closure_body : & hir:: Body < ' _ > ,
158
+ ) -> Option < String > {
159
+ let mut visitor = DerefDelegate {
160
+ cx,
161
+ closure_span : search_arg. span ,
162
+ next_pos : None ,
163
+ suggestion_start : String :: new ( ) ,
164
+ suggestion_end : String :: new ( ) ,
165
+ } ;
166
+
167
+ let fn_def_id = cx. tcx . hir ( ) . local_def_id ( search_arg. hir_id ) ;
168
+ cx. tcx . infer_ctxt ( ) . enter ( |infcx| {
169
+ ExprUseVisitor :: new ( & mut visitor, & infcx, fn_def_id, cx. param_env , cx. typeck_results ( ) )
170
+ . consume_body ( closure_body) ;
171
+ } ) ;
172
+
173
+ if visitor. suggestion_start . is_empty ( ) {
174
+ None
175
+ } else {
176
+ Some ( format ! ( "{}{}" , visitor. suggestion_start, visitor. suggestion_end) )
177
+ }
178
+ }
179
+
187
180
struct DerefDelegate < ' a , ' tcx > {
188
181
cx : & ' a LateContext < ' tcx > ,
189
- set : HirIdSet ,
190
- deref_suggs : HirIdMap < String > ,
191
- borrow_suggs : HirIdMap < String > ,
182
+ closure_span : Span ,
183
+ next_pos : Option < BytePos > ,
184
+ suggestion_start : String ,
185
+ suggestion_end : String ,
192
186
}
193
187
194
188
impl < ' tcx > Delegate < ' tcx > for DerefDelegate < ' _ , ' tcx > {
195
- fn consume ( & mut self , cmt : & PlaceWithHirId < ' tcx > , _: HirId ) {
189
+ fn consume ( & mut self , _: & PlaceWithHirId < ' tcx > , _: HirId ) { }
190
+
191
+ fn borrow ( & mut self , cmt : & PlaceWithHirId < ' tcx > , _: HirId , _: ty:: BorrowKind ) {
196
192
if let PlaceBase :: Local ( id) = cmt. place . base {
197
193
let map = self . cx . tcx . hir ( ) ;
198
- if cmt. place . projections . is_empty ( ) {
199
- self . set . insert ( cmt. hir_id ) ;
194
+ let ident_str = map. name ( id) . to_string ( ) ;
195
+ let span = map. span ( cmt. hir_id ) ;
196
+ let start_span = if let Some ( next_pos) = self . next_pos {
197
+ Span :: new ( next_pos, span. lo ( ) , span. ctxt ( ) )
200
198
} else {
201
- let mut replacement_str = map. name ( id) . to_string ( ) ;
202
- let last_deref = cmt
203
- . place
204
- . projections
205
- . iter ( )
206
- . rposition ( |proj| proj. kind == ProjectionKind :: Deref ) ;
199
+ self . closure_span . until ( span)
200
+ } ;
201
+ let start_snip = snippet ( self . cx , start_span, ".." ) ;
202
+ let end_span = Span :: new ( span. hi ( ) , self . closure_span . hi ( ) , span. ctxt ( ) ) ;
203
+ let end_snip = snippet ( self . cx , end_span, ".." ) ;
207
204
208
- if let Some ( pos) = last_deref {
209
- let mut projections = cmt. place . projections . clone ( ) ;
210
- projections. truncate ( pos) ;
205
+ if cmt. place . projections . is_empty ( ) {
206
+ self . suggestion_start . push_str ( & format ! ( "{}&{}" , start_snip, ident_str) ) ;
207
+ } else {
208
+ let parent_expr = get_parent_expr_for_hir ( self . cx , cmt. hir_id ) ;
209
+ if let Some ( Expr { hir_id : _, kind, .. } ) = parent_expr {
210
+ if let ExprKind :: Call ( _, args) | ExprKind :: MethodCall ( _, _, args, _) = kind {
211
+ let args_to_handle = args. iter ( ) . filter ( |arg| arg. hir_id == cmt. hir_id ) . collect :: < Vec < _ > > ( ) ;
212
+ if !args_to_handle. is_empty ( ) {
213
+ for arg in & args_to_handle {
214
+ let arg_ty_kind = self . cx . typeck_results ( ) . expr_ty ( arg) . kind ( ) ;
215
+ if matches ! ( arg_ty_kind, ty:: Ref ( _, _, Mutability :: Not ) ) {
216
+ let start_span = if let Some ( next_pos) = self . next_pos {
217
+ Span :: new ( next_pos, span. lo ( ) , span. ctxt ( ) )
218
+ } else {
219
+ self . closure_span . until ( span)
220
+ } ;
221
+ let start_snip = snippet ( self . cx , start_span, ".." ) ;
211
222
212
- for item in projections {
213
- if item. kind == ProjectionKind :: Deref {
214
- replacement_str = format ! ( "*{}" , replacement_str) ;
223
+ self . suggestion_start . push_str ( & format ! ( "{}&{}" , start_snip, ident_str) ) ;
224
+ self . suggestion_end = end_snip. to_string ( ) ;
225
+ self . next_pos = Some ( span. hi ( ) ) ;
226
+ }
227
+ }
228
+ return ;
215
229
}
216
230
}
217
-
218
- self . set . insert ( cmt. hir_id ) ;
219
- self . deref_suggs . insert ( cmt. hir_id , replacement_str) ;
220
231
}
221
- }
222
- }
223
- }
224
232
225
- fn borrow ( & mut self , cmt : & PlaceWithHirId < ' tcx > , _: HirId , _: ty:: BorrowKind ) {
226
- if let PlaceBase :: Local ( id) = cmt. place . base {
227
- let map = self . cx . tcx . hir ( ) ;
228
- if cmt. place . projections . is_empty ( ) {
229
- let replacement_str = format ! ( "&{}" , map. name( id) . to_string( ) ) ;
230
- self . set . insert ( cmt. hir_id ) ;
231
- self . borrow_suggs . insert ( cmt. hir_id , replacement_str) ;
232
- } else {
233
- let mut replacement_str = map. name ( id) . to_string ( ) ;
233
+ let mut replacement_str = ident_str;
234
234
let last_deref = cmt
235
235
. place
236
236
. projections
@@ -246,11 +246,13 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
246
246
replacement_str = format ! ( "*{}" , replacement_str) ;
247
247
}
248
248
}
249
-
250
- self . set . insert ( cmt. hir_id ) ;
251
- self . deref_suggs . insert ( cmt. hir_id , replacement_str) ;
252
249
}
250
+
251
+ self . suggestion_start
252
+ . push_str ( & format ! ( "{}{}" , start_snip, replacement_str) ) ;
253
+ self . suggestion_end = end_snip. to_string ( ) ;
253
254
}
255
+ self . next_pos = Some ( span. hi ( ) ) ;
254
256
}
255
257
}
256
258
0 commit comments