@@ -60,6 +60,7 @@ mod uninit_assumed_init;
6060mod unnecessary_filter_map;
6161mod unnecessary_fold;
6262mod unnecessary_iter_cloned;
63+ mod unnecessary_join;
6364mod unnecessary_lazy_eval;
6465mod unnecessary_to_owned;
6566mod unwrap_or_else_default;
@@ -2049,6 +2050,35 @@ declare_clippy_lint! {
20492050 "unnecessary calls to `to_owned`-like functions"
20502051}
20512052
2053+ declare_clippy_lint ! {
2054+ /// ### What it does
2055+ /// Checks for use of `.collect::<Vec<String>>().join("")` on iterators.
2056+ ///
2057+ /// ### Why is this bad?
2058+ /// `.collect::<String>()` is more concise and usually more performant
2059+ ///
2060+ /// ### Example
2061+ /// ```rust
2062+ /// let vector = vec!["hello", "world"];
2063+ /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<Vec<String>>().join("");
2064+ /// println!("{}", output);
2065+ /// ```
2066+ /// The correct use would be:
2067+ /// ```rust
2068+ /// let vector = vec!["hello", "world"];
2069+ /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
2070+ /// println!("{}", output);
2071+ /// ```
2072+ /// ### Known problems
2073+ /// While `.collect::<String>()` is more performant in most cases, there are cases where
2074+ /// using `.collect::<String>()` over `.collect::<Vec<String>>().join("")`
2075+ /// will prevent loop unrolling and will result in a negative performance impact.
2076+ #[ clippy:: version = "1.61.0" ]
2077+ pub UNNECESSARY_JOIN ,
2078+ pedantic,
2079+ "using `.collect::<Vec<String>>().join(\" \" )` on an iterator"
2080+ }
2081+
20522082pub struct Methods {
20532083 avoid_breaking_exported_api : bool ,
20542084 msrv : Option < RustcVersion > ,
@@ -2134,6 +2164,7 @@ impl_lint_pass!(Methods => [
21342164 MANUAL_SPLIT_ONCE ,
21352165 NEEDLESS_SPLITN ,
21362166 UNNECESSARY_TO_OWNED ,
2167+ UNNECESSARY_JOIN ,
21372168] ) ;
21382169
21392170/// Extracts a method call name, args, and `Span` of the method name.
@@ -2429,6 +2460,11 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
24292460 ( "is_file" , [ ] ) => filetype_is_file:: check ( cx, expr, recv) ,
24302461 ( "is_none" , [ ] ) => check_is_some_is_none ( cx, expr, recv, false ) ,
24312462 ( "is_some" , [ ] ) => check_is_some_is_none ( cx, expr, recv, true ) ,
2463+ ( "join" , [ join_arg] ) => {
2464+ if let Some ( ( "collect" , _, span) ) = method_call ( recv) {
2465+ unnecessary_join:: check ( cx, expr, recv, join_arg, span) ;
2466+ }
2467+ } ,
24322468 ( "last" , args @ [ ] ) | ( "skip" , args @ [ _] ) => {
24332469 if let Some ( ( name2, [ recv2, args2 @ ..] , _span2) ) = method_call ( recv) {
24342470 if let ( "cloned" , [ ] ) = ( name2, args2) {
0 commit comments