@@ -30,6 +30,7 @@ mod iter_count;
30
30
mod iter_next_slice;
31
31
mod iter_nth;
32
32
mod iter_nth_zero;
33
+ mod iter_overeager_cloned;
33
34
mod iter_skip_next;
34
35
mod iterator_step_by_zero;
35
36
mod manual_saturating_arithmetic;
@@ -106,6 +107,41 @@ declare_clippy_lint! {
106
107
"used `cloned` where `copied` could be used instead"
107
108
}
108
109
110
+ declare_clippy_lint ! {
111
+ /// ### What it does
112
+ /// Checks for usage of `_.cloned().<func>()` where call to `.cloned()` can be postponed.
113
+ ///
114
+ /// ### Why is this bad?
115
+ /// It's often inefficient to clone all elements of an iterator, when eventually, only some
116
+ /// of them will be consumed.
117
+ ///
118
+ /// ### Examples
119
+ /// ```rust
120
+ /// # let vec = vec!["string".to_string()];
121
+ ///
122
+ /// // Bad
123
+ /// vec.iter().cloned().take(10);
124
+ ///
125
+ /// // Good
126
+ /// vec.iter().take(10).cloned();
127
+ ///
128
+ /// // Bad
129
+ /// vec.iter().cloned().last();
130
+ ///
131
+ /// // Good
132
+ /// vec.iter().last().cloned();
133
+ ///
134
+ /// ```
135
+ /// ### Known Problems
136
+ /// This `lint` removes the side of effect of cloning items in the iterator.
137
+ /// A code that relies on that side-effect could fail.
138
+ ///
139
+ #[ clippy:: version = "1.59.0" ]
140
+ pub ITER_OVEREAGER_CLONED ,
141
+ perf,
142
+ "using `cloned()` early with `Iterator::iter()` can lead to some performance inefficiencies"
143
+ }
144
+
109
145
declare_clippy_lint ! {
110
146
/// ### What it does
111
147
/// Checks for usages of `Iterator::flat_map()` where `filter_map()` could be
@@ -1950,6 +1986,7 @@ impl_lint_pass!(Methods => [
1950
1986
CLONE_ON_COPY ,
1951
1987
CLONE_ON_REF_PTR ,
1952
1988
CLONE_DOUBLE_REF ,
1989
+ ITER_OVEREAGER_CLONED ,
1953
1990
CLONED_INSTEAD_OF_COPIED ,
1954
1991
FLAT_MAP_OPTION ,
1955
1992
INEFFICIENT_TO_STRING ,
@@ -2243,9 +2280,10 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
2243
2280
} ,
2244
2281
_ => { } ,
2245
2282
} ,
2246
- ( "count" , [ ] ) => match method_call ( recv) {
2247
- Some ( ( name @ ( "into_iter" | "iter" | "iter_mut" ) , [ recv2] , _) ) => {
2248
- iter_count:: check ( cx, expr, recv2, name) ;
2283
+ ( name @ "count" , args @ [ ] ) => match method_call ( recv) {
2284
+ Some ( ( "cloned" , [ recv2] , _) ) => iter_overeager_cloned:: check ( cx, expr, recv2, name, args) ,
2285
+ Some ( ( name2 @ ( "into_iter" | "iter" | "iter_mut" ) , [ recv2] , _) ) => {
2286
+ iter_count:: check ( cx, expr, recv2, name2) ;
2249
2287
} ,
2250
2288
Some ( ( "map" , [ _, arg] , _) ) => suspicious_map:: check ( cx, expr, recv, arg) ,
2251
2289
_ => { } ,
@@ -2266,10 +2304,10 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
2266
2304
flat_map_identity:: check ( cx, expr, arg, span) ;
2267
2305
flat_map_option:: check ( cx, expr, arg, span) ;
2268
2306
} ,
2269
- ( "flatten" , [ ] ) => {
2270
- if let Some ( ( "map" , [ recv, map_arg] , _) ) = method_call ( recv) {
2271
- map_flatten :: check ( cx, expr, recv , map_arg ) ;
2272
- }
2307
+ ( name @ "flatten" , args @ [ ] ) => match method_call ( recv ) {
2308
+ Some ( ( "map" , [ recv, map_arg] , _) ) => map_flatten :: check ( cx , expr , recv, map_arg ) ,
2309
+ Some ( ( "cloned" , [ recv2 ] , _ ) ) => iter_overeager_cloned :: check ( cx, expr, recv2 , name , args ) ,
2310
+ _ => { } ,
2273
2311
} ,
2274
2312
( "fold" , [ init, acc] ) => unnecessary_fold:: check ( cx, expr, init, acc, span) ,
2275
2313
( "for_each" , [ _] ) => {
@@ -2281,6 +2319,13 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
2281
2319
( "is_file" , [ ] ) => filetype_is_file:: check ( cx, expr, recv) ,
2282
2320
( "is_none" , [ ] ) => check_is_some_is_none ( cx, expr, recv, false ) ,
2283
2321
( "is_some" , [ ] ) => check_is_some_is_none ( cx, expr, recv, true ) ,
2322
+ ( "last" , args @ [ ] ) => {
2323
+ if let Some ( ( name2, [ recv2, args2 @ ..] , _span2) ) = method_call ( recv) {
2324
+ if let ( "cloned" , [ ] ) = ( name2, args2) {
2325
+ iter_overeager_cloned:: check ( cx, expr, recv2, name, args) ;
2326
+ }
2327
+ }
2328
+ } ,
2284
2329
( "map" , [ m_arg] ) => {
2285
2330
if let Some ( ( name, [ recv2, args @ ..] , span2) ) = method_call ( recv) {
2286
2331
match ( name, args) {
@@ -2296,20 +2341,22 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
2296
2341
map_identity:: check ( cx, expr, recv, m_arg, span) ;
2297
2342
} ,
2298
2343
( "map_or" , [ def, map] ) => option_map_or_none:: check ( cx, expr, recv, def, map) ,
2299
- ( "next" , [ ] ) => {
2300
- if let Some ( ( name, [ recv, args @ ..] , _) ) = method_call ( recv) {
2301
- match ( name, args) {
2302
- ( "filter" , [ arg] ) => filter_next:: check ( cx, expr, recv, arg) ,
2303
- ( "filter_map" , [ arg] ) => filter_map_next:: check ( cx, expr, recv, arg, msrv) ,
2304
- ( "iter" , [ ] ) => iter_next_slice:: check ( cx, expr, recv) ,
2305
- ( "skip" , [ arg] ) => iter_skip_next:: check ( cx, expr, recv, arg) ,
2344
+ ( name @ "next" , args @ [ ] ) => {
2345
+ if let Some ( ( name2, [ recv2, args2 @ ..] , _) ) = method_call ( recv) {
2346
+ match ( name2, args2) {
2347
+ ( "cloned" , [ ] ) => iter_overeager_cloned:: check ( cx, expr, recv2, name, args) ,
2348
+ ( "filter" , [ arg] ) => filter_next:: check ( cx, expr, recv2, arg) ,
2349
+ ( "filter_map" , [ arg] ) => filter_map_next:: check ( cx, expr, recv2, arg, msrv) ,
2350
+ ( "iter" , [ ] ) => iter_next_slice:: check ( cx, expr, recv2) ,
2351
+ ( "skip" , [ arg] ) => iter_skip_next:: check ( cx, expr, recv2, arg) ,
2306
2352
( "skip_while" , [ _] ) => skip_while_next:: check ( cx, expr) ,
2307
2353
_ => { } ,
2308
2354
}
2309
2355
}
2310
2356
} ,
2311
- ( "nth" , [ n_arg] ) => match method_call ( recv) {
2357
+ ( "nth" , args @ [ n_arg] ) => match method_call ( recv) {
2312
2358
Some ( ( "bytes" , [ recv2] , _) ) => bytes_nth:: check ( cx, expr, recv2, n_arg) ,
2359
+ Some ( ( "cloned" , [ recv2] , _) ) => iter_overeager_cloned:: check ( cx, expr, recv2, name, args) ,
2313
2360
Some ( ( "iter" , [ recv2] , _) ) => iter_nth:: check ( cx, expr, recv2, recv, n_arg, false ) ,
2314
2361
Some ( ( "iter_mut" , [ recv2] , _) ) => iter_nth:: check ( cx, expr, recv2, recv, n_arg, true ) ,
2315
2362
_ => iter_nth_zero:: check ( cx, expr, recv, n_arg) ,
@@ -2320,6 +2367,13 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
2320
2367
unnecessary_lazy_eval:: check ( cx, expr, recv, arg, "or" ) ;
2321
2368
}
2322
2369
} ,
2370
+ ( "skip" , args) => {
2371
+ if let Some ( ( name2, [ recv2, args2 @ ..] , _span2) ) = method_call ( recv) {
2372
+ if let ( "cloned" , [ ] ) = ( name2, args2) {
2373
+ iter_overeager_cloned:: check ( cx, expr, recv2, name, args) ;
2374
+ }
2375
+ }
2376
+ } ,
2323
2377
( "splitn" | "rsplitn" , [ count_arg, pat_arg] ) => {
2324
2378
if let Some ( ( Constant :: Int ( count) , _) ) = constant ( cx, cx. typeck_results ( ) , count_arg) {
2325
2379
suspicious_splitn:: check ( cx, name, expr, recv, count) ;
@@ -2337,6 +2391,13 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
2337
2391
}
2338
2392
} ,
2339
2393
( "step_by" , [ arg] ) => iterator_step_by_zero:: check ( cx, expr, arg) ,
2394
+ ( "take" , args @ [ _arg] ) => {
2395
+ if let Some ( ( name2, [ recv2, args2 @ ..] , _span2) ) = method_call ( recv) {
2396
+ if let ( "cloned" , [ ] ) = ( name2, args2) {
2397
+ iter_overeager_cloned:: check ( cx, expr, recv2, name, args) ;
2398
+ }
2399
+ }
2400
+ } ,
2340
2401
( "to_os_string" | "to_owned" | "to_path_buf" | "to_vec" , [ ] ) => {
2341
2402
implicit_clone:: check ( cx, name, expr, recv) ;
2342
2403
} ,
0 commit comments