Skip to content

Commit

Permalink
Improve needless_collect output
Browse files Browse the repository at this point in the history
  • Loading branch information
camsteffen committed Apr 2, 2021
1 parent 4356a8f commit 33798bb
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 27 deletions.
11 changes: 6 additions & 5 deletions clippy_lints/src/loops/needless_collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{Block, Expr, ExprKind, GenericArg, HirId, Local, Pat, PatKind, QPath, StmtKind};
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
use rustc_span::source_map::Span;
use rustc_span::symbol::{sym, Ident};
use rustc_span::{MultiSpan, Span};

const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";

Expand Down Expand Up @@ -65,7 +65,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo
Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. },
init: Some(ref init_expr), .. }
) = stmt.kind;
if let ExprKind::MethodCall(ref method_name, _, &[ref iter_source], ..) = init_expr.kind;
if let ExprKind::MethodCall(ref method_name, collect_span, &[ref iter_source], ..) = init_expr.kind;
if method_name.ident.name == sym!(collect) && is_trait_method(cx, &init_expr, sym::Iterator);
if let Some(ref generic_args) = method_name.args;
if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
Expand All @@ -74,7 +74,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo
is_type_diagnostic_item(cx, ty, sym::vecdeque_type) ||
match_type(cx, ty, &paths::LINKED_LIST);
if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident);
if iter_calls.len() == 1;
if let [iter_call] = &*iter_calls;
then {
let mut used_count_visitor = UsedCountVisitor {
cx,
Expand All @@ -87,11 +87,12 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo
}

// Suggest replacing iter_call with iter_replacement, and removing stmt
let iter_call = &iter_calls[0];
let mut span = MultiSpan::from_span(collect_span);
span.push_span_label(iter_call.span, "the iterator could be used here instead".into());
span_lint_and_then(
cx,
super::NEEDLESS_COLLECT,
stmt.span.until(iter_call.span),
span,
NEEDLESS_COLLECT_MSG,
|diag| {
let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx));
Expand Down
6 changes: 4 additions & 2 deletions clippy_utils/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,11 @@ pub fn span_lint_and_note<'a, T: LintContext>(
///
/// If you need to customize your lint output a lot, use this function.
/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F)
pub fn span_lint_and_then<C, S, F>(cx: &C, lint: &'static Lint, sp: S, msg: &str, f: F)
where
F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
C: LintContext,
S: Into<MultiSpan>,
F: FnOnce(&mut DiagnosticBuilder<'_>),
{
cx.struct_span_lint(lint, sp, |diag| {
let mut diag = diag.build(msg);
Expand Down
45 changes: 25 additions & 20 deletions tests/ui/needless_collect_indirect.stderr
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
error: avoid using `collect()` when not needed
--> $DIR/needless_collect_indirect.rs:5:5
--> $DIR/needless_collect_indirect.rs:5:39
|
LL | / let indirect_iter = sample.iter().collect::<Vec<_>>();
LL | | indirect_iter.into_iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
| |____^
LL | let indirect_iter = sample.iter().collect::<Vec<_>>();
| ^^^^^^^
LL | indirect_iter.into_iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
| ------------------------- the iterator could be used here instead
|
= note: `-D clippy::needless-collect` implied by `-D warnings`
help: use the original Iterator instead of collecting it and then producing a new one
Expand All @@ -13,11 +14,12 @@ LL | sample.iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
|

error: avoid using `collect()` when not needed
--> $DIR/needless_collect_indirect.rs:7:5
--> $DIR/needless_collect_indirect.rs:7:38
|
LL | / let indirect_len = sample.iter().collect::<VecDeque<_>>();
LL | | indirect_len.len();
| |____^
LL | let indirect_len = sample.iter().collect::<VecDeque<_>>();
| ^^^^^^^
LL | indirect_len.len();
| ------------------ the iterator could be used here instead
|
help: take the original Iterator's count instead of collecting it and finding the length
|
Expand All @@ -26,11 +28,12 @@ LL | sample.iter().count();
|

error: avoid using `collect()` when not needed
--> $DIR/needless_collect_indirect.rs:9:5
--> $DIR/needless_collect_indirect.rs:9:40
|
LL | / let indirect_empty = sample.iter().collect::<VecDeque<_>>();
LL | | indirect_empty.is_empty();
| |____^
LL | let indirect_empty = sample.iter().collect::<VecDeque<_>>();
| ^^^^^^^
LL | indirect_empty.is_empty();
| ------------------------- the iterator could be used here instead
|
help: check if the original Iterator has anything instead of collecting it and seeing if it's empty
|
Expand All @@ -39,11 +42,12 @@ LL | sample.iter().next().is_none();
|

error: avoid using `collect()` when not needed
--> $DIR/needless_collect_indirect.rs:11:5
--> $DIR/needless_collect_indirect.rs:11:43
|
LL | / let indirect_contains = sample.iter().collect::<VecDeque<_>>();
LL | | indirect_contains.contains(&&5);
| |____^
LL | let indirect_contains = sample.iter().collect::<VecDeque<_>>();
| ^^^^^^^
LL | indirect_contains.contains(&&5);
| ------------------------------- the iterator could be used here instead
|
help: check if the original Iterator contains an element instead of collecting then checking
|
Expand All @@ -52,11 +56,12 @@ LL | sample.iter().any(|x| x == &5);
|

error: avoid using `collect()` when not needed
--> $DIR/needless_collect_indirect.rs:23:5
--> $DIR/needless_collect_indirect.rs:23:48
|
LL | / let non_copy_contains = sample.into_iter().collect::<Vec<_>>();
LL | | non_copy_contains.contains(&a);
| |____^
LL | let non_copy_contains = sample.into_iter().collect::<Vec<_>>();
| ^^^^^^^
LL | non_copy_contains.contains(&a);
| ------------------------------ the iterator could be used here instead
|
help: check if the original Iterator contains an element instead of collecting then checking
|
Expand Down

0 comments on commit 33798bb

Please sign in to comment.