@@ -26,6 +26,7 @@ mod filter_map_next;
2626mod filter_next;
2727mod flat_map_identity;
2828mod flat_map_option;
29+ mod format_collect;
2930mod from_iter_instead_of_collect;
3031mod get_first;
3132mod get_last_with_len;
@@ -3378,6 +3379,36 @@ declare_clippy_lint! {
33783379 "calling `Stdin::read_line`, then trying to parse it without first trimming"
33793380}
33803381
3382+ declare_clippy_lint ! {
3383+ /// ### What it does
3384+ /// Checks for usage of `.map(|_| format!(..)).collect::<String>()`.
3385+ ///
3386+ /// ### Why is this bad?
3387+ /// This allocates a new string for every element in the iterator.
3388+ /// This can be done more efficiently by creating the `String` once and appending to it using `Iterator::fold`.
3389+ ///
3390+ /// ### Example
3391+ /// ```rust
3392+ /// fn hex_encode(bytes: &[u8]) -> String {
3393+ /// bytes.iter().map(|b| format!("{b:02X}")).collect()
3394+ /// }
3395+ /// ```
3396+ /// Use instead:
3397+ /// ```rust
3398+ /// use std::fmt::Write;
3399+ /// fn hex_encode(bytes: &[u8]) -> String {
3400+ /// bytes.iter().fold(String::new(), |mut output, b| {
3401+ /// let _ = write!(output, "{b:02X}");
3402+ /// output
3403+ /// })
3404+ /// }
3405+ /// ```
3406+ #[ clippy:: version = "1.72.0" ]
3407+ pub FORMAT_COLLECT ,
3408+ perf,
3409+ "`format!`ing every element in a collection, then collecting the strings into a new `String`"
3410+ }
3411+
33813412pub struct Methods {
33823413 avoid_breaking_exported_api : bool ,
33833414 msrv : Msrv ,
@@ -3512,6 +3543,7 @@ impl_lint_pass!(Methods => [
35123543 UNNECESSARY_LITERAL_UNWRAP ,
35133544 DRAIN_COLLECT ,
35143545 MANUAL_TRY_FOLD ,
3546+ FORMAT_COLLECT ,
35153547] ) ;
35163548
35173549/// Extracts a method call name, args, and `Span` of the method name.
@@ -3733,8 +3765,9 @@ impl Methods {
37333765 Some ( ( name @ ( "cloned" | "copied" ) , recv2, [ ] , _, _) ) => {
37343766 iter_cloned_collect:: check ( cx, name, expr, recv2) ;
37353767 } ,
3736- Some ( ( "map" , m_recv, [ m_arg] , _ , _) ) => {
3768+ Some ( ( "map" , m_recv, [ m_arg] , m_ident_span , _) ) => {
37373769 map_collect_result_unit:: check ( cx, expr, m_recv, m_arg) ;
3770+ format_collect:: check ( cx, expr, m_arg, m_ident_span) ;
37383771 } ,
37393772 Some ( ( "take" , take_self_arg, [ take_arg] , _, _) ) => {
37403773 if self . msrv . meets ( msrvs:: STR_REPEAT ) {
0 commit comments