Skip to content

Commit 3657c92

Browse files
committed
Check for other things which can be used indirectly
1 parent 3ee6137 commit 3657c92

File tree

1 file changed

+62
-32
lines changed

1 file changed

+62
-32
lines changed

clippy_lints/src/loops.rs

Lines changed: 62 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2429,7 +2429,6 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
24292429
if let ExprKind::Block(ref block, _) = expr.kind {
24302430
for ref stmt in block.stmts {
24312431
if_chain! {
2432-
// TODO also work for assignments to an existing variable
24332432
if let StmtKind::Local(
24342433
Local { pat: Pat { kind: PatKind::Binding(_, _, ident, .. ), .. },
24352434
init: Some(ref init_expr), .. }
@@ -2446,21 +2445,22 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
24462445
if iter_calls.len() == 1;
24472446
then {
24482447
// Suggest replacing iter_call with iter_replacement, and removing stmt
2448+
let iter_call = &iter_calls[0];
24492449
span_lint_and_then(
24502450
cx,
24512451
NEEDLESS_COLLECT,
2452-
stmt.span,
2452+
stmt.span.until(iter_call.span),
24532453
NEEDLESS_COLLECT_MSG,
24542454
|diag| {
2455-
let iter_replacement = Sugg::hir(cx, iter_source, "..").to_string();
2455+
let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx));
24562456
diag.multipart_suggestion(
2457-
"Use the original Iterator instead of collecting it and then producing a new one",
2457+
iter_call.get_suggestion_text(),
24582458
vec![
24592459
(stmt.span, String::new()),
2460-
(iter_calls[0].span, iter_replacement)
2460+
(iter_call.span, iter_replacement)
24612461
],
2462-
Applicability::MaybeIncorrect,
2463-
);
2462+
Applicability::MachineApplicable,// MaybeIncorrect,
2463+
).emit();
24642464
},
24652465
);
24662466
}
@@ -2469,32 +2469,62 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
24692469
}
24702470
}
24712471

2472-
struct IntoIterVisitor<'tcx> {
2473-
iters: Vec<&'tcx Expr<'tcx>>,
2472+
struct IterFunction {
2473+
func: IterFunctionKind,
2474+
span: Span,
2475+
}
2476+
impl IterFunction {
2477+
fn get_iter_method(&self, cx: &LateContext<'_>) -> String {
2478+
match &self.func {
2479+
IterFunctionKind::IntoIter => String::new(),
2480+
IterFunctionKind::Len => String::from(".count()"),
2481+
IterFunctionKind::IsEmpty => String::from(".next().is_none()"),
2482+
IterFunctionKind::Contains(span) => format!(".any(|x| x == {})", snippet(cx, *span, "..")),
2483+
}
2484+
}
2485+
fn get_suggestion_text(&self) -> &'static str {
2486+
match &self.func {
2487+
IterFunctionKind::IntoIter => "Use the original Iterator instead of collecting it and then producing a new one",
2488+
IterFunctionKind::Len => "Take the original Iterator's count instead of collecting it and finding the length",
2489+
IterFunctionKind::IsEmpty => "Check if the original Iterator has anything instead of collecting it and seeing if it's empty",
2490+
IterFunctionKind::Contains(_) => "Check if the original Iterator contains an element instead of collecting then checking",
2491+
}
2492+
}
2493+
}
2494+
enum IterFunctionKind {
2495+
IntoIter,
2496+
Len,
2497+
IsEmpty,
2498+
Contains(Span),
2499+
}
2500+
2501+
struct IterFunctionVisitor {
2502+
uses: Vec<IterFunction>,
24742503
seen_other: bool,
24752504
target: String,
24762505
}
2477-
impl<'tcx> Visitor<'tcx> for IntoIterVisitor<'tcx> {
2506+
impl<'tcx> Visitor<'tcx> for IterFunctionVisitor {
24782507
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
2479-
match &expr.kind {
2480-
ExprKind::MethodCall(
2481-
method_name,
2482-
_,
2483-
&[Expr {
2484-
kind: ExprKind::Path(QPath::Resolved(_, ref path)),
2485-
..
2486-
}],
2487-
_,
2488-
) if match_path(path, &[&self.target]) => {
2489-
// TODO Check what method is being called, if it's called on target, and act
2490-
// accordingly
2491-
if method_name.ident.name == sym!(into_iter) {
2492-
self.iters.push(expr);
2493-
} else {
2494-
self.seen_other = true;
2508+
if_chain! {
2509+
if let ExprKind::MethodCall(method_name, _, ref args, _) = &expr.kind;
2510+
if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. }) = args.get(0);
2511+
if match_path(path, &[&self.target]);
2512+
then {
2513+
let into_iter = sym!(into_iter);
2514+
let len = sym!(len);
2515+
let is_empty = sym!(is_empty);
2516+
let contains = sym!(contains);
2517+
match method_name.ident.name {
2518+
name if name == into_iter => self.uses.push(IterFunction { func: IterFunctionKind::IntoIter, span: expr.span }),
2519+
name if name == len => self.uses.push(IterFunction { func: IterFunctionKind::Len, span: expr.span }),
2520+
name if name == is_empty => self.uses.push(IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span }),
2521+
name if name == contains => self.uses.push(IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span }),
2522+
_ => self.seen_other = true,
24952523
}
2496-
},
2497-
_ => walk_expr(self, expr),
2524+
}
2525+
else {
2526+
walk_expr(self, expr);
2527+
}
24982528
}
24992529
}
25002530

@@ -2506,17 +2536,17 @@ impl<'tcx> Visitor<'tcx> for IntoIterVisitor<'tcx> {
25062536

25072537
/// Detect the occurences of calls to `iter` or `into_iter` for the
25082538
/// given identifier
2509-
fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option<Vec<&'tcx Expr<'tcx>>> {
2510-
let mut visitor = IntoIterVisitor {
2511-
iters: Vec::new(),
2539+
fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option<Vec<IterFunction>> {
2540+
let mut visitor = IterFunctionVisitor {
2541+
uses: Vec::new(),
25122542
target: identifier.name.to_ident_string(),
25132543
seen_other: false,
25142544
};
25152545
visitor.visit_block(block);
25162546
if visitor.seen_other {
25172547
None
25182548
} else {
2519-
Some(visitor.iters)
2549+
Some(visitor.uses)
25202550
}
25212551
}
25222552

0 commit comments

Comments
 (0)