unnecessary_join lint

This commit is contained in:
Yoav Lavi 2022-03-24 13:18:18 +01:00 committed by GitHub
parent f07ee8a998
commit b60a7fb7b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 172 additions and 0 deletions

View file

@ -3508,6 +3508,7 @@ Released 2018-09-13
[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
[`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map
[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation

View file

@ -334,6 +334,7 @@ store.register_lints(&[
methods::UNNECESSARY_FILTER_MAP,
methods::UNNECESSARY_FIND_MAP,
methods::UNNECESSARY_FOLD,
methods::UNNECESSARY_JOIN,
methods::UNNECESSARY_LAZY_EVALUATIONS,
methods::UNNECESSARY_TO_OWNED,
methods::UNWRAP_OR_ELSE_DEFAULT,

View file

@ -63,6 +63,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
LintId::of(methods::IMPLICIT_CLONE),
LintId::of(methods::INEFFICIENT_TO_STRING),
LintId::of(methods::MAP_UNWRAP_OR),
LintId::of(methods::UNNECESSARY_JOIN),
LintId::of(misc::FLOAT_CMP),
LintId::of(misc::USED_UNDERSCORE_BINDING),
LintId::of(mut_mut::MUT_MUT),

View file

@ -60,6 +60,7 @@ mod uninit_assumed_init;
mod unnecessary_filter_map;
mod unnecessary_fold;
mod unnecessary_iter_cloned;
mod unnecessary_join;
mod unnecessary_lazy_eval;
mod unnecessary_to_owned;
mod unwrap_or_else_default;
@ -2049,6 +2050,35 @@ declare_clippy_lint! {
"unnecessary calls to `to_owned`-like functions"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for use of `.collect::<Vec<String>>().join("")` on iterators.
///
/// ### Why is this bad?
/// `.collect::<String>()` is more concise and usually more performant
///
/// ### Example
/// ```rust
/// let vector = vec!["hello", "world"];
/// let output = vector.iter().map(|item| item.to_uppercase()).collect::<Vec<String>>().join("");
/// println!("{}", output);
/// ```
/// The correct use would be:
/// ```rust
/// let vector = vec!["hello", "world"];
/// let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
/// println!("{}", output);
/// ```
/// ### Known problems
/// While `.collect::<String>()` is more performant in most cases, there are cases where
/// using `.collect::<String>()` over `.collect::<Vec<String>>().join("")`
/// will prevent loop unrolling and will result in a negative performance impact.
#[clippy::version = "1.61.0"]
pub UNNECESSARY_JOIN,
pedantic,
"using `.collect::<Vec<String>>().join(\"\")` on an iterator"
}
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Option<RustcVersion>,
@ -2134,6 +2164,7 @@ impl_lint_pass!(Methods => [
MANUAL_SPLIT_ONCE,
NEEDLESS_SPLITN,
UNNECESSARY_TO_OWNED,
UNNECESSARY_JOIN,
]);
/// 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
("is_file", []) => filetype_is_file::check(cx, expr, recv),
("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
("join", [join_arg]) => {
if let Some(("collect", _, span)) = method_call(recv) {
unnecessary_join::check(cx, expr, recv, join_arg, span);
}
},
("last", args @ []) | ("skip", args @ [_]) => {
if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
if let ("cloned", []) = (name2, args2) {

View file

@ -0,0 +1,41 @@
use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{Ref, Slice};
use rustc_span::{sym, Span};
use super::UNNECESSARY_JOIN;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
join_self_arg: &'tcx Expr<'tcx>,
join_arg: &'tcx Expr<'tcx>,
span: Span,
) {
let applicability = Applicability::MachineApplicable;
let collect_output_adjusted_type = cx.typeck_results().expr_ty_adjusted(join_self_arg);
if_chain! {
// the turbofish for collect is ::<Vec<String>>
if let Ref(_, ref_type, _) = collect_output_adjusted_type.kind();
if let Slice(slice) = ref_type.kind();
if is_type_diagnostic_item(cx, *slice, sym::String);
// the argument for join is ""
if let ExprKind::Lit(spanned) = &join_arg.kind;
if let LitKind::Str(symbol, _) = spanned.node;
if symbol.is_empty();
then {
span_lint_and_sugg(
cx,
UNNECESSARY_JOIN,
span.with_hi(expr.span.hi()),
r#"called `.collect<Vec<String>>().join("")` on an iterator"#,
"try using",
"collect::<String>()".to_owned(),
applicability,
);
}
}
}

View file

@ -0,0 +1,35 @@
// run-rustfix
#![warn(clippy::unnecessary_join)]
fn main() {
// should be linted
let vector = vec!["hello", "world"];
let output = vector
.iter()
.map(|item| item.to_uppercase())
.collect::<String>();
println!("{}", output);
// should be linted
let vector = vec!["hello", "world"];
let output = vector
.iter()
.map(|item| item.to_uppercase())
.collect::<String>();
println!("{}", output);
// should not be linted
let vector = vec!["hello", "world"];
let output = vector
.iter()
.map(|item| item.to_uppercase())
.collect::<Vec<String>>()
.join("\n");
println!("{}", output);
// should not be linted
let vector = vec!["hello", "world"];
let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
println!("{}", output);
}

View file

@ -0,0 +1,37 @@
// run-rustfix
#![warn(clippy::unnecessary_join)]
fn main() {
// should be linted
let vector = vec!["hello", "world"];
let output = vector
.iter()
.map(|item| item.to_uppercase())
.collect::<Vec<String>>()
.join("");
println!("{}", output);
// should be linted
let vector = vec!["hello", "world"];
let output = vector
.iter()
.map(|item| item.to_uppercase())
.collect::<Vec<_>>()
.join("");
println!("{}", output);
// should not be linted
let vector = vec!["hello", "world"];
let output = vector
.iter()
.map(|item| item.to_uppercase())
.collect::<Vec<String>>()
.join("\n");
println!("{}", output);
// should not be linted
let vector = vec!["hello", "world"];
let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
println!("{}", output);
}

View file

@ -0,0 +1,20 @@
error: called `.collect<Vec<String>>().join("")` on an iterator
--> $DIR/unnecessary_join.rs:11:10
|
LL | .collect::<Vec<String>>()
| __________^
LL | | .join("");
| |_________________^ help: try using: `collect::<String>()`
|
= note: `-D clippy::unnecessary-join` implied by `-D warnings`
error: called `.collect<Vec<String>>().join("")` on an iterator
--> $DIR/unnecessary_join.rs:20:10
|
LL | .collect::<Vec<_>>()
| __________^
LL | | .join("");
| |_________________^ help: try using: `collect::<String>()`
error: aborting due to 2 previous errors