@@ -30,6 +30,7 @@ mod iter_count;
3030mod iter_next_slice;
3131mod iter_nth;
3232mod iter_nth_zero;
33+ mod iter_overeager_cloned;
3334mod iter_skip_next;
3435mod iterator_step_by_zero;
3536mod manual_saturating_arithmetic;
@@ -106,6 +107,41 @@ declare_clippy_lint! {
106107 "used `cloned` where `copied` could be used instead"
107108}
108109
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+
109145declare_clippy_lint ! {
110146 /// ### What it does
111147 /// Checks for usages of `Iterator::flat_map()` where `filter_map()` could be
@@ -1950,6 +1986,7 @@ impl_lint_pass!(Methods => [
19501986 CLONE_ON_COPY ,
19511987 CLONE_ON_REF_PTR ,
19521988 CLONE_DOUBLE_REF ,
1989+ ITER_OVEREAGER_CLONED ,
19531990 CLONED_INSTEAD_OF_COPIED ,
19541991 FLAT_MAP_OPTION ,
19551992 INEFFICIENT_TO_STRING ,
@@ -2243,9 +2280,10 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
22432280 } ,
22442281 _ => { } ,
22452282 } ,
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) ;
22492287 } ,
22502288 Some ( ( "map" , [ _, arg] , _) ) => suspicious_map:: check ( cx, expr, recv, arg) ,
22512289 _ => { } ,
@@ -2266,10 +2304,10 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
22662304 flat_map_identity:: check ( cx, expr, arg, span) ;
22672305 flat_map_option:: check ( cx, expr, arg, span) ;
22682306 } ,
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+ _ => { } ,
22732311 } ,
22742312 ( "fold" , [ init, acc] ) => unnecessary_fold:: check ( cx, expr, init, acc, span) ,
22752313 ( "for_each" , [ _] ) => {
@@ -2281,6 +2319,13 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
22812319 ( "is_file" , [ ] ) => filetype_is_file:: check ( cx, expr, recv) ,
22822320 ( "is_none" , [ ] ) => check_is_some_is_none ( cx, expr, recv, false ) ,
22832321 ( "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+ } ,
22842329 ( "map" , [ m_arg] ) => {
22852330 if let Some ( ( name, [ recv2, args @ ..] , span2) ) = method_call ( recv) {
22862331 match ( name, args) {
@@ -2296,20 +2341,22 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
22962341 map_identity:: check ( cx, expr, recv, m_arg, span) ;
22972342 } ,
22982343 ( "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) ,
23062352 ( "skip_while" , [ _] ) => skip_while_next:: check ( cx, expr) ,
23072353 _ => { } ,
23082354 }
23092355 }
23102356 } ,
2311- ( "nth" , [ n_arg] ) => match method_call ( recv) {
2357+ ( "nth" , args @ [ n_arg] ) => match method_call ( recv) {
23122358 Some ( ( "bytes" , [ recv2] , _) ) => bytes_nth:: check ( cx, expr, recv2, n_arg) ,
2359+ Some ( ( "cloned" , [ recv2] , _) ) => iter_overeager_cloned:: check ( cx, expr, recv2, name, args) ,
23132360 Some ( ( "iter" , [ recv2] , _) ) => iter_nth:: check ( cx, expr, recv2, recv, n_arg, false ) ,
23142361 Some ( ( "iter_mut" , [ recv2] , _) ) => iter_nth:: check ( cx, expr, recv2, recv, n_arg, true ) ,
23152362 _ => 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
23202367 unnecessary_lazy_eval:: check ( cx, expr, recv, arg, "or" ) ;
23212368 }
23222369 } ,
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+ } ,
23232377 ( "splitn" | "rsplitn" , [ count_arg, pat_arg] ) => {
23242378 if let Some ( ( Constant :: Int ( count) , _) ) = constant ( cx, cx. typeck_results ( ) , count_arg) {
23252379 suspicious_splitn:: check ( cx, name, expr, recv, count) ;
@@ -2337,6 +2391,13 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
23372391 }
23382392 } ,
23392393 ( "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+ } ,
23402401 ( "to_os_string" | "to_owned" | "to_path_buf" | "to_vec" , [ ] ) => {
23412402 implicit_clone:: check ( cx, name, expr, recv) ;
23422403 } ,
0 commit comments