1
1
use clippy_utils:: consts:: { constant, Constant } ;
2
2
use clippy_utils:: diagnostics:: span_lint_and_sugg;
3
3
use clippy_utils:: source:: snippet_with_context;
4
- use clippy_utils:: { is_diag_item_method, match_def_path, paths} ;
4
+ use clippy_utils:: { is_diag_item_method, match_def_path, meets_msrv , msrvs , paths} ;
5
5
use if_chain:: if_chain;
6
6
use rustc_errors:: Applicability ;
7
7
use rustc_hir:: { Expr , ExprKind , HirId , LangItem , Node , QPath } ;
8
8
use rustc_lint:: LateContext ;
9
- use rustc_middle:: ty:: { self , adjustment:: Adjust } ;
9
+ use rustc_middle:: ty;
10
+ use rustc_semver:: RustcVersion ;
10
11
use rustc_span:: { symbol:: sym, Span , SyntaxContext } ;
11
12
12
- use super :: MANUAL_SPLIT_ONCE ;
13
+ use super :: { MANUAL_SPLIT_ONCE , NEEDLESS_SPLITN } ;
13
14
14
- pub ( super ) fn check_manual_split_once (
15
+ pub ( super ) fn check (
15
16
cx : & LateContext < ' _ > ,
16
17
method_name : & str ,
17
18
expr : & Expr < ' _ > ,
18
19
self_arg : & Expr < ' _ > ,
19
20
pat_arg : & Expr < ' _ > ,
21
+ count : u128 ,
22
+ msrv : Option < & RustcVersion > ,
20
23
) {
21
- if !cx. typeck_results ( ) . expr_ty_adjusted ( self_arg) . peel_refs ( ) . is_str ( ) {
24
+ if count < 2 || !cx. typeck_results ( ) . expr_ty_adjusted ( self_arg) . peel_refs ( ) . is_str ( ) {
22
25
return ;
23
26
}
24
27
25
28
let ctxt = expr. span . ctxt ( ) ;
26
- let ( method_name, msg, reverse) = if method_name == "splitn" {
27
- ( "split_once" , "manual implementation of `split_once`" , false )
28
- } else {
29
- ( "rsplit_once" , "manual implementation of `rsplit_once`" , true )
29
+ let Some ( usage) = parse_iter_usage ( cx, ctxt, cx. tcx . hir ( ) . parent_iter ( expr. hir_id ) ) else { return } ;
30
+
31
+ let needless = match usage. kind {
32
+ IterUsageKind :: Nth ( n) => count > n + 1 ,
33
+ IterUsageKind :: NextTuple => count > 2 ,
30
34
} ;
31
- let usage = match parse_iter_usage ( cx, ctxt, cx. tcx . hir ( ) . parent_iter ( expr. hir_id ) , reverse) {
32
- Some ( x) => x,
33
- None => return ,
35
+
36
+ if needless {
37
+ let mut app = Applicability :: MachineApplicable ;
38
+ let ( r, message) = if method_name == "splitn" {
39
+ ( "" , "unnecessary use of `splitn`" )
40
+ } else {
41
+ ( "r" , "unnecessary use of `rsplitn`" )
42
+ } ;
43
+
44
+ span_lint_and_sugg (
45
+ cx,
46
+ NEEDLESS_SPLITN ,
47
+ expr. span ,
48
+ message,
49
+ "try this" ,
50
+ format ! (
51
+ "{}.{r}split({})" ,
52
+ snippet_with_context( cx, self_arg. span, ctxt, ".." , & mut app) . 0 ,
53
+ snippet_with_context( cx, pat_arg. span, ctxt, ".." , & mut app) . 0 ,
54
+ ) ,
55
+ app,
56
+ ) ;
57
+ } else if count == 2 && meets_msrv ( msrv, & msrvs:: STR_SPLIT_ONCE ) {
58
+ check_manual_split_once ( cx, method_name, expr, self_arg, pat_arg, & usage) ;
59
+ }
60
+ }
61
+
62
+ fn check_manual_split_once (
63
+ cx : & LateContext < ' _ > ,
64
+ method_name : & str ,
65
+ expr : & Expr < ' _ > ,
66
+ self_arg : & Expr < ' _ > ,
67
+ pat_arg : & Expr < ' _ > ,
68
+ usage : & IterUsage ,
69
+ ) {
70
+ let ctxt = expr. span . ctxt ( ) ;
71
+ let ( msg, reverse) = if method_name == "splitn" {
72
+ ( "manual implementation of `split_once`" , false )
73
+ } else {
74
+ ( "manual implementation of `rsplit_once`" , true )
34
75
} ;
35
76
36
77
let mut app = Applicability :: MachineApplicable ;
@@ -39,77 +80,36 @@ pub(super) fn check_manual_split_once(
39
80
40
81
let sugg = match usage. kind {
41
82
IterUsageKind :: NextTuple => {
42
- format ! ( "{}.{}({})" , self_snip, method_name, pat_snip)
43
- } ,
44
- IterUsageKind :: RNextTuple => format ! ( "{}.{}({}).map(|(x, y)| (y, x))" , self_snip, method_name, pat_snip) ,
45
- IterUsageKind :: Next | IterUsageKind :: Second => {
46
- let self_deref = {
47
- let adjust = cx. typeck_results ( ) . expr_adjustments ( self_arg) ;
48
- if adjust. len ( ) < 2 {
49
- String :: new ( )
50
- } else if cx. typeck_results ( ) . expr_ty ( self_arg) . is_box ( )
51
- || adjust
52
- . iter ( )
53
- . any ( |a| matches ! ( a. kind, Adjust :: Deref ( Some ( _) ) ) || a. target . is_box ( ) )
54
- {
55
- format ! ( "&{}" , "*" . repeat( adjust. len( ) . saturating_sub( 1 ) ) )
56
- } else {
57
- "*" . repeat ( adjust. len ( ) . saturating_sub ( 2 ) )
58
- }
59
- } ;
60
- if matches ! ( usage. kind, IterUsageKind :: Next ) {
61
- match usage. unwrap_kind {
62
- Some ( UnwrapKind :: Unwrap ) => {
63
- if reverse {
64
- format ! ( "{}.{}({}).unwrap().0" , self_snip, method_name, pat_snip)
65
- } else {
66
- format ! (
67
- "{}.{}({}).map_or({}{}, |x| x.0)" ,
68
- self_snip, method_name, pat_snip, self_deref, & self_snip
69
- )
70
- }
71
- } ,
72
- Some ( UnwrapKind :: QuestionMark ) => {
73
- format ! (
74
- "{}.{}({}).map_or({}{}, |x| x.0)" ,
75
- self_snip, method_name, pat_snip, self_deref, & self_snip
76
- )
77
- } ,
78
- None => {
79
- format ! (
80
- "Some({}.{}({}).map_or({}{}, |x| x.0))" ,
81
- & self_snip, method_name, pat_snip, self_deref, & self_snip
82
- )
83
- } ,
84
- }
83
+ if reverse {
84
+ format ! ( "{self_snip}.rsplit_once({pat_snip}).map(|(x, y)| (y, x))" )
85
85
} else {
86
- match usage. unwrap_kind {
87
- Some ( UnwrapKind :: Unwrap ) => {
88
- if reverse {
89
- // In this case, no better suggestion is offered.
90
- return ;
91
- }
92
- format ! ( "{}.{}({}).unwrap().1" , self_snip, method_name, pat_snip)
93
- } ,
94
- Some ( UnwrapKind :: QuestionMark ) => {
95
- format ! ( "{}.{}({})?.1" , self_snip, method_name, pat_snip)
96
- } ,
97
- None => {
98
- format ! ( "{}.{}({}).map(|x| x.1)" , self_snip, method_name, pat_snip)
99
- } ,
100
- }
86
+ format ! ( "{self_snip}.split_once({pat_snip})" )
101
87
}
102
88
} ,
89
+ IterUsageKind :: Nth ( 1 ) => {
90
+ let ( r, field) = if reverse { ( "r" , 0 ) } else { ( "" , 1 ) } ;
91
+
92
+ match usage. unwrap_kind {
93
+ Some ( UnwrapKind :: Unwrap ) => {
94
+ format ! ( "{self_snip}.{r}split_once({pat_snip}).unwrap().{field}" )
95
+ } ,
96
+ Some ( UnwrapKind :: QuestionMark ) => {
97
+ format ! ( "{self_snip}.{r}split_once({pat_snip})?.{field}" )
98
+ } ,
99
+ None => {
100
+ format ! ( "{self_snip}.{r}split_once({pat_snip}).map(|x| x.{field})" )
101
+ } ,
102
+ }
103
+ } ,
104
+ IterUsageKind :: Nth ( _) => return ,
103
105
} ;
104
106
105
107
span_lint_and_sugg ( cx, MANUAL_SPLIT_ONCE , usage. span , msg, "try this" , sugg, app) ;
106
108
}
107
109
108
110
enum IterUsageKind {
109
- Next ,
110
- Second ,
111
+ Nth ( u128 ) ,
111
112
NextTuple ,
112
- RNextTuple ,
113
113
}
114
114
115
115
enum UnwrapKind {
@@ -128,7 +128,6 @@ fn parse_iter_usage<'tcx>(
128
128
cx : & LateContext < ' tcx > ,
129
129
ctxt : SyntaxContext ,
130
130
mut iter : impl Iterator < Item = ( HirId , Node < ' tcx > ) > ,
131
- reverse : bool ,
132
131
) -> Option < IterUsage > {
133
132
let ( kind, span) = match iter. next ( ) {
134
133
Some ( ( _, Node :: Expr ( e) ) ) if e. span . ctxt ( ) == ctxt => {
@@ -141,13 +140,7 @@ fn parse_iter_usage<'tcx>(
141
140
let iter_id = cx. tcx . get_diagnostic_item ( sym:: Iterator ) ?;
142
141
143
142
match ( name. ident . as_str ( ) , args) {
144
- ( "next" , [ ] ) if cx. tcx . trait_of_item ( did) == Some ( iter_id) => {
145
- if reverse {
146
- ( IterUsageKind :: Second , e. span )
147
- } else {
148
- ( IterUsageKind :: Next , e. span )
149
- }
150
- } ,
143
+ ( "next" , [ ] ) if cx. tcx . trait_of_item ( did) == Some ( iter_id) => ( IterUsageKind :: Nth ( 0 ) , e. span ) ,
151
144
( "next_tuple" , [ ] ) => {
152
145
return if_chain ! {
153
146
if match_def_path( cx, did, & paths:: ITERTOOLS_NEXT_TUPLE ) ;
@@ -157,7 +150,7 @@ fn parse_iter_usage<'tcx>(
157
150
if subs. len( ) == 2 ;
158
151
then {
159
152
Some ( IterUsage {
160
- kind: if reverse { IterUsageKind :: RNextTuple } else { IterUsageKind :: NextTuple } ,
153
+ kind: IterUsageKind :: NextTuple ,
161
154
span: e. span,
162
155
unwrap_kind: None
163
156
} )
@@ -185,11 +178,7 @@ fn parse_iter_usage<'tcx>(
185
178
}
186
179
}
187
180
} ;
188
- match if reverse { idx ^ 1 } else { idx } {
189
- 0 => ( IterUsageKind :: Next , span) ,
190
- 1 => ( IterUsageKind :: Second , span) ,
191
- _ => return None ,
192
- }
181
+ ( IterUsageKind :: Nth ( idx) , span)
193
182
} else {
194
183
return None ;
195
184
}
@@ -238,86 +227,3 @@ fn parse_iter_usage<'tcx>(
238
227
span,
239
228
} )
240
229
}
241
-
242
- use super :: NEEDLESS_SPLITN ;
243
-
244
- pub ( super ) fn check_needless_splitn (
245
- cx : & LateContext < ' _ > ,
246
- method_name : & str ,
247
- expr : & Expr < ' _ > ,
248
- self_arg : & Expr < ' _ > ,
249
- pat_arg : & Expr < ' _ > ,
250
- count : u128 ,
251
- ) {
252
- if !cx. typeck_results ( ) . expr_ty_adjusted ( self_arg) . peel_refs ( ) . is_str ( ) {
253
- return ;
254
- }
255
- let ctxt = expr. span . ctxt ( ) ;
256
- let mut app = Applicability :: MachineApplicable ;
257
- let ( reverse, message) = if method_name == "splitn" {
258
- ( false , "unnecessary use of `splitn`" )
259
- } else {
260
- ( true , "unnecessary use of `rsplitn`" )
261
- } ;
262
- if_chain ! {
263
- if count >= 2 ;
264
- if check_iter( cx, ctxt, cx. tcx. hir( ) . parent_iter( expr. hir_id) , count) ;
265
- then {
266
- span_lint_and_sugg(
267
- cx,
268
- NEEDLESS_SPLITN ,
269
- expr. span,
270
- message,
271
- "try this" ,
272
- format!(
273
- "{}.{}({})" ,
274
- snippet_with_context( cx, self_arg. span, ctxt, ".." , & mut app) . 0 ,
275
- if reverse { "rsplit" } else { "split" } ,
276
- snippet_with_context( cx, pat_arg. span, ctxt, ".." , & mut app) . 0
277
- ) ,
278
- app,
279
- ) ;
280
- }
281
- }
282
- }
283
-
284
- fn check_iter < ' tcx > (
285
- cx : & LateContext < ' tcx > ,
286
- ctxt : SyntaxContext ,
287
- mut iter : impl Iterator < Item = ( HirId , Node < ' tcx > ) > ,
288
- count : u128 ,
289
- ) -> bool {
290
- match iter. next ( ) {
291
- Some ( ( _, Node :: Expr ( e) ) ) if e. span . ctxt ( ) == ctxt => {
292
- let ( name, args) = if let ExprKind :: MethodCall ( name, [ _, args @ ..] , _) = e. kind {
293
- ( name, args)
294
- } else {
295
- return false ;
296
- } ;
297
- if_chain ! {
298
- if let Some ( did) = cx. typeck_results( ) . type_dependent_def_id( e. hir_id) ;
299
- if let Some ( iter_id) = cx. tcx. get_diagnostic_item( sym:: Iterator ) ;
300
- then {
301
- match ( name. ident. as_str( ) , args) {
302
- ( "next" , [ ] ) if cx. tcx. trait_of_item( did) == Some ( iter_id) => {
303
- return true ;
304
- } ,
305
- ( "next_tuple" , [ ] ) if count > 2 => {
306
- return true ;
307
- } ,
308
- ( "nth" , [ idx_expr] ) if cx. tcx. trait_of_item( did) == Some ( iter_id) => {
309
- if let Some ( ( Constant :: Int ( idx) , _) ) = constant( cx, cx. typeck_results( ) , idx_expr) {
310
- if count > idx + 1 {
311
- return true ;
312
- }
313
- }
314
- } ,
315
- _ => return false ,
316
- }
317
- }
318
- }
319
- } ,
320
- _ => return false ,
321
- } ;
322
- false
323
- }
0 commit comments