@@ -11,7 +11,13 @@ use rustc_span::{symbol::sym, Span, SyntaxContext};
11
11
12
12
use super :: MANUAL_SPLIT_ONCE ;
13
13
14
- pub ( super ) fn check ( cx : & LateContext < ' _ > , method_name : & str , expr : & Expr < ' _ > , self_arg : & Expr < ' _ > , pat_arg : & Expr < ' _ > ) {
14
+ pub ( super ) fn check_manual_split_once (
15
+ cx : & LateContext < ' _ > ,
16
+ method_name : & str ,
17
+ expr : & Expr < ' _ > ,
18
+ self_arg : & Expr < ' _ > ,
19
+ pat_arg : & Expr < ' _ > ,
20
+ ) {
15
21
if !cx. typeck_results ( ) . expr_ty_adjusted ( self_arg) . peel_refs ( ) . is_str ( ) {
16
22
return ;
17
23
}
@@ -36,7 +42,7 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se
36
42
format ! ( "{}.{}({})" , self_snip, method_name, pat_snip)
37
43
} ,
38
44
IterUsageKind :: RNextTuple => format ! ( "{}.{}({}).map(|(x, y)| (y, x))" , self_snip, method_name, pat_snip) ,
39
- IterUsageKind :: Next => {
45
+ IterUsageKind :: Next | IterUsageKind :: Second => {
40
46
let self_deref = {
41
47
let adjust = cx. typeck_results ( ) . expr_adjustments ( self_arg) ;
42
48
if adjust. is_empty ( ) {
@@ -51,26 +57,49 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se
51
57
"*" . repeat ( adjust. len ( ) - 2 )
52
58
}
53
59
} ;
54
- if usage. unwrap_kind . is_some ( ) {
55
- format ! (
56
- "{}.{}({}).map_or({}{}, |x| x.0)" ,
57
- & self_snip, method_name, pat_snip, self_deref, & self_snip
58
- )
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
+ }
59
85
} else {
60
- format ! (
61
- "Some({}.{}({}).map_or({}{}, |x| x.0))" ,
62
- & self_snip, method_name, pat_snip, self_deref, & self_snip
63
- )
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
+ }
64
101
}
65
102
} ,
66
- IterUsageKind :: Second => {
67
- let access_str = match usage. unwrap_kind {
68
- Some ( UnwrapKind :: Unwrap ) => ".unwrap().1" ,
69
- Some ( UnwrapKind :: QuestionMark ) => "?.1" ,
70
- None => ".map(|x| x.1)" ,
71
- } ;
72
- format ! ( "{}.{}({}){}" , self_snip, method_name, pat_snip, access_str)
73
- } ,
74
103
} ;
75
104
76
105
span_lint_and_sugg ( cx, MANUAL_SPLIT_ONCE , usage. span , msg, "try this" , sugg, app) ;
@@ -209,3 +238,86 @@ fn parse_iter_usage(
209
238
span,
210
239
} )
211
240
}
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 (
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