@@ -60,6 +60,7 @@ mod uninit_assumed_init;
60
60
mod unnecessary_filter_map;
61
61
mod unnecessary_fold;
62
62
mod unnecessary_iter_cloned;
63
+ mod unnecessary_join;
63
64
mod unnecessary_lazy_eval;
64
65
mod unnecessary_to_owned;
65
66
mod unwrap_or_else_default;
@@ -2049,6 +2050,35 @@ declare_clippy_lint! {
2049
2050
"unnecessary calls to `to_owned`-like functions"
2050
2051
}
2051
2052
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
+
2052
2082
pub struct Methods {
2053
2083
avoid_breaking_exported_api : bool ,
2054
2084
msrv : Option < RustcVersion > ,
@@ -2134,6 +2164,7 @@ impl_lint_pass!(Methods => [
2134
2164
MANUAL_SPLIT_ONCE ,
2135
2165
NEEDLESS_SPLITN ,
2136
2166
UNNECESSARY_TO_OWNED ,
2167
+ UNNECESSARY_JOIN ,
2137
2168
] ) ;
2138
2169
2139
2170
/// 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
2429
2460
( "is_file" , [ ] ) => filetype_is_file:: check ( cx, expr, recv) ,
2430
2461
( "is_none" , [ ] ) => check_is_some_is_none ( cx, expr, recv, false ) ,
2431
2462
( "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
+ } ,
2432
2468
( "last" , args @ [ ] ) | ( "skip" , args @ [ _] ) => {
2433
2469
if let Some ( ( name2, [ recv2, args2 @ ..] , _span2) ) = method_call ( recv) {
2434
2470
if let ( "cloned" , [ ] ) = ( name2, args2) {
0 commit comments