1
- use clippy_utils:: diagnostics:: { span_lint_and_help, span_lint_and_sugg} ;
1
+ use clippy_utils:: diagnostics:: { span_lint_and_help, span_lint_and_sugg, span_lint_and_then } ;
2
2
use clippy_utils:: source:: { snippet, snippet_with_applicability} ;
3
3
use clippy_utils:: ty:: is_type_diagnostic_item;
4
4
use clippy_utils:: { 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:: PatKind ;
8
+ use rustc_hir:: { self , HirId , HirIdMap , HirIdSet , PatKind } ;
9
+ use rustc_infer:: infer:: TyCtxtInferExt ;
9
10
use rustc_lint:: LateContext ;
11
+ use rustc_middle:: hir:: place:: ProjectionKind ;
12
+ use rustc_middle:: mir:: FakeReadCause ;
10
13
use rustc_middle:: ty;
11
14
use rustc_span:: source_map:: Span ;
12
15
use rustc_span:: symbol:: sym;
16
+ use rustc_typeck:: expr_use_visitor:: { Delegate , ExprUseVisitor , PlaceBase , PlaceWithHirId } ;
13
17
14
18
use super :: SEARCH_IS_SOME ;
15
19
@@ -42,30 +46,42 @@ pub(super) fn check<'tcx>(
42
46
if let hir:: ExprKind :: Closure ( _, _, body_id, ..) = search_arg. kind;
43
47
let closure_body = cx. tcx. hir( ) . body( body_id) ;
44
48
if let Some ( closure_arg) = closure_body. params. get( 0 ) ;
49
+
45
50
then {
46
51
if let hir:: PatKind :: Ref ( ..) = closure_arg. pat. kind {
47
- Some ( search_snippet. replacen( '&' , "" , 1 ) )
48
- } else if let PatKind :: Binding ( annotation, _, ident, _) = strip_pat_refs( closure_arg. pat) . kind {
49
- let name = & * ident. name. as_str( ) ;
50
- let old_search_snippet = search_snippet. clone( ) ;
51
- let search_snippet = search_snippet. replace( & format!( "*{}" , name) , name) ;
52
-
53
- if_chain! {
54
- // if there is no dereferencing used in closure body
55
- if old_search_snippet == search_snippet;
56
- if annotation == hir:: BindingAnnotation :: Unannotated ;
57
- if let ty:: Ref ( _, inner_ty, _) = cx. typeck_results( ) . node_type( closure_arg. hir_id) . kind( ) ;
58
- if let ty:: Ref ( ..) = inner_ty. kind( ) ;
59
- // put an `&` in the closure body, but skip closure params
60
- if let Some ( ( start, end) ) = old_search_snippet. split_once( & name) ;
61
-
62
- then {
63
- let end = end. replace( name, & format!( "&{}" , name) ) ;
64
- Some ( format!( "{}{}{}" , start, name, end) )
65
- } else {
66
- Some ( search_snippet)
52
+ Some ( ( search_snippet. replacen( '&' , "" , 1 ) , None ) )
53
+ } 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
+ }
67
81
}
68
- }
82
+ Some ( ( deref_suggs, borrow_suggs) )
83
+ } ;
84
+ Some ( ( search_snippet. to_string( ) , replacements) )
69
85
} else {
70
86
None
71
87
}
@@ -74,35 +90,38 @@ pub(super) fn check<'tcx>(
74
90
}
75
91
} ;
76
92
// add note if not multi-line
77
- if is_some {
78
- span_lint_and_sugg (
79
- cx,
80
- SEARCH_IS_SOME ,
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
+ (
81
98
method_span. with_hi ( expr. span . hi ( ) ) ,
82
- & msg,
83
99
"use `any()` instead" ,
84
- format ! (
85
- "any({})" ,
86
- any_search_snippet. as_ref( ) . map_or( & * search_snippet, String :: as_str)
87
- ) ,
88
- Applicability :: MachineApplicable ,
89
- ) ;
100
+ format ! ( "any({})" , closure_snippet) ,
101
+ )
90
102
} else {
91
103
let iter = snippet ( cx, search_recv. span , ".." ) ;
92
- span_lint_and_sugg (
93
- cx,
94
- SEARCH_IS_SOME ,
104
+ (
95
105
expr. span ,
96
- & msg,
97
106
"use `!_.any()` instead" ,
98
- format ! (
99
- "!{}.any({})" ,
100
- iter,
101
- any_search_snippet. as_ref( ) . map_or( & * search_snippet, String :: as_str)
102
- ) ,
103
- Applicability :: MachineApplicable ,
104
- ) ;
105
- }
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
+ } ) ;
106
125
} else {
107
126
let hint = format ! (
108
127
"this is more succinctly expressed by calling `any()`{}" ,
@@ -164,3 +183,78 @@ pub(super) fn check<'tcx>(
164
183
}
165
184
}
166
185
}
186
+
187
+ struct DerefDelegate < ' a , ' tcx > {
188
+ cx : & ' a LateContext < ' tcx > ,
189
+ set : HirIdSet ,
190
+ deref_suggs : HirIdMap < String > ,
191
+ borrow_suggs : HirIdMap < String > ,
192
+ }
193
+
194
+ impl < ' tcx > Delegate < ' tcx > for DerefDelegate < ' _ , ' tcx > {
195
+ fn consume ( & mut self , cmt : & PlaceWithHirId < ' tcx > , _: HirId ) {
196
+ if let PlaceBase :: Local ( id) = cmt. place . base {
197
+ let map = self . cx . tcx . hir ( ) ;
198
+ if cmt. place . projections . is_empty ( ) {
199
+ self . set . insert ( cmt. hir_id ) ;
200
+ } 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 ) ;
207
+
208
+ if let Some ( pos) = last_deref {
209
+ let mut projections = cmt. place . projections . clone ( ) ;
210
+ projections. truncate ( pos) ;
211
+
212
+ for item in projections {
213
+ if item. kind == ProjectionKind :: Deref {
214
+ replacement_str = format ! ( "*{}" , replacement_str) ;
215
+ }
216
+ }
217
+
218
+ self . set . insert ( cmt. hir_id ) ;
219
+ self . deref_suggs . insert ( cmt. hir_id , replacement_str) ;
220
+ }
221
+ }
222
+ }
223
+ }
224
+
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 ( ) ;
234
+ let last_deref = cmt
235
+ . place
236
+ . projections
237
+ . iter ( )
238
+ . rposition ( |proj| proj. kind == ProjectionKind :: Deref ) ;
239
+
240
+ if let Some ( pos) = last_deref {
241
+ let mut projections = cmt. place . projections . clone ( ) ;
242
+ projections. truncate ( pos) ;
243
+
244
+ for item in projections {
245
+ if item. kind == ProjectionKind :: Deref {
246
+ replacement_str = format ! ( "*{}" , replacement_str) ;
247
+ }
248
+ }
249
+
250
+ self . set . insert ( cmt. hir_id ) ;
251
+ self . deref_suggs . insert ( cmt. hir_id , replacement_str) ;
252
+ }
253
+ }
254
+ }
255
+ }
256
+
257
+ fn mutate ( & mut self , _: & PlaceWithHirId < ' tcx > , _: HirId ) { }
258
+
259
+ fn fake_read ( & mut self , _: rustc_typeck:: expr_use_visitor:: Place < ' tcx > , _: FakeReadCause , _: HirId ) { }
260
+ }
0 commit comments