Skip to content

Commit 74bd806

Browse files
committed
Simplify check_for_loop_arg
1 parent eaf63d6 commit 74bd806

File tree

4 files changed

+90
-67
lines changed

4 files changed

+90
-67
lines changed

clippy_lints/src/loops/explicit_into_iter_loop.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,17 @@ use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
33
use rustc_errors::Applicability;
44
use rustc_hir::Expr;
55
use rustc_lint::LateContext;
6+
use rustc_middle::ty::TyS;
7+
8+
pub(super) fn check(cx: &LateContext<'_>, args: &'hir [Expr<'hir>], arg: &Expr<'_>) {
9+
let receiver_ty = cx.typeck_results().expr_ty(&args[0]);
10+
let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]);
11+
if !TyS::same_type(receiver_ty, receiver_ty_adjusted) {
12+
return;
13+
}
614

7-
pub(super) fn check(cx: &LateContext<'_>, method_args: &'hir [Expr<'hir>], arg: &Expr<'_>) {
815
let mut applicability = Applicability::MachineApplicable;
9-
let object = snippet_with_applicability(cx, method_args[0].span, "_", &mut applicability);
16+
let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
1017
span_lint_and_sugg(
1118
cx,
1219
EXPLICIT_INTO_ITER_LOOP,

clippy_lints/src/loops/explicit_iter_loop.rs

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,35 @@
11
use super::EXPLICIT_ITER_LOOP;
2-
use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
2+
use crate::utils::{match_trait_method, snippet_with_applicability, span_lint_and_sugg};
33
use rustc_errors::Applicability;
4-
use rustc_hir::Expr;
4+
use rustc_hir::{Expr, Mutability};
55
use rustc_lint::LateContext;
6+
use rustc_middle::ty::{self, Ty, TyS};
7+
use rustc_span::symbol::sym;
8+
9+
use crate::utils::{is_type_diagnostic_item, match_type, paths};
610

711
pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) {
12+
let should_lint = match method_name {
13+
"iter" | "iter_mut" => is_ref_iterable_type(cx, &args[0]),
14+
"into_iter" if match_trait_method(cx, arg, &paths::INTO_ITERATOR) => {
15+
let receiver_ty = cx.typeck_results().expr_ty(&args[0]);
16+
let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]);
17+
let ref_receiver_ty = cx.tcx.mk_ref(
18+
cx.tcx.lifetimes.re_erased,
19+
ty::TypeAndMut {
20+
ty: receiver_ty,
21+
mutbl: Mutability::Not,
22+
},
23+
);
24+
TyS::same_type(receiver_ty_adjusted, ref_receiver_ty)
25+
},
26+
_ => false,
27+
};
28+
29+
if !should_lint {
30+
return;
31+
}
32+
833
let mut applicability = Applicability::MachineApplicable;
934
let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
1035
let muta = if method_name == "iter_mut" { "mut " } else { "" };
@@ -19,3 +44,31 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, met
1944
applicability,
2045
)
2146
}
47+
48+
/// Returns `true` if the type of expr is one that provides `IntoIterator` impls
49+
/// for `&T` and `&mut T`, such as `Vec`.
50+
#[rustfmt::skip]
51+
fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
52+
// no walk_ptrs_ty: calling iter() on a reference can make sense because it
53+
// will allow further borrows afterwards
54+
let ty = cx.typeck_results().expr_ty(e);
55+
is_iterable_array(ty, cx) ||
56+
is_type_diagnostic_item(cx, ty, sym::vec_type) ||
57+
match_type(cx, ty, &paths::LINKED_LIST) ||
58+
is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) ||
59+
is_type_diagnostic_item(cx, ty, sym!(hashset_type)) ||
60+
is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) ||
61+
match_type(cx, ty, &paths::BINARY_HEAP) ||
62+
match_type(cx, ty, &paths::BTREEMAP) ||
63+
match_type(cx, ty, &paths::BTREESET)
64+
}
65+
66+
fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool {
67+
// IntoIterator is currently only implemented for array sizes <= 32 in rustc
68+
match ty.kind() {
69+
ty::Array(_, n) => n
70+
.try_eval_usize(cx.tcx, cx.param_env)
71+
.map_or(false, |val| (0..=32).contains(&val)),
72+
_ => false,
73+
}
74+
}
Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
use super::ITER_NEXT_LOOP;
2-
use crate::utils::span_lint;
2+
use crate::utils::{match_trait_method, paths, span_lint};
33
use rustc_hir::Expr;
44
use rustc_lint::LateContext;
55

6-
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
7-
span_lint(
8-
cx,
9-
ITER_NEXT_LOOP,
10-
expr.span,
11-
"you are iterating over `Iterator::next()` which is an Option; this will compile but is \
12-
probably not what you want",
13-
);
6+
pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, expr: &Expr<'_>) -> bool {
7+
if match_trait_method(cx, arg, &paths::ITERATOR) {
8+
span_lint(
9+
cx,
10+
ITER_NEXT_LOOP,
11+
expr.span,
12+
"you are iterating over `Iterator::next()` which is an Option; this will compile but is \
13+
probably not what you want",
14+
);
15+
true
16+
} else {
17+
false
18+
}
1419
}

clippy_lints/src/loops/mod.rs

Lines changed: 12 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,11 @@ mod while_immutable_condition;
1818
mod while_let_loop;
1919
mod while_let_on_iterator;
2020

21-
use crate::utils::{higher, is_type_diagnostic_item, match_trait_method, match_type, paths};
22-
use rustc_hir::{Expr, ExprKind, LoopSource, Mutability, Pat};
21+
use crate::utils::higher;
22+
use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
2323
use rustc_lint::{LateContext, LateLintPass};
24-
use rustc_middle::ty::{self, Ty, TyS};
2524
use rustc_session::{declare_lint_pass, declare_tool_lint};
2625
use rustc_span::source_map::Span;
27-
use rustc_span::symbol::sym;
2826
use utils::{get_span_of_entire_for_loop, make_iterator_snippet, IncrementVisitor, InitializeVisitor};
2927

3028
declare_clippy_lint! {
@@ -603,67 +601,27 @@ fn check_for_loop<'tcx>(
603601

604602
fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: &Expr<'_>) {
605603
let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used
604+
606605
if let ExprKind::MethodCall(ref method, _, ref args, _) = arg.kind {
607606
// just the receiver, no arguments
608607
if args.len() == 1 {
609608
let method_name = &*method.ident.as_str();
610609
// check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
611-
if method_name == "iter" || method_name == "iter_mut" {
612-
if is_ref_iterable_type(cx, &args[0]) {
610+
match method_name {
611+
"iter" | "iter_mut" => explicit_iter_loop::check(cx, args, arg, method_name),
612+
"into_iter" => {
613613
explicit_iter_loop::check(cx, args, arg, method_name);
614-
}
615-
} else if method_name == "into_iter" && match_trait_method(cx, arg, &paths::INTO_ITERATOR) {
616-
let receiver_ty = cx.typeck_results().expr_ty(&args[0]);
617-
let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]);
618-
if TyS::same_type(receiver_ty, receiver_ty_adjusted) {
619614
explicit_into_iter_loop::check(cx, args, arg);
620-
} else {
621-
let ref_receiver_ty = cx.tcx.mk_ref(
622-
cx.tcx.lifetimes.re_erased,
623-
ty::TypeAndMut {
624-
ty: receiver_ty,
625-
mutbl: Mutability::Not,
626-
},
627-
);
628-
if TyS::same_type(receiver_ty_adjusted, ref_receiver_ty) {
629-
explicit_iter_loop::check(cx, args, arg, method_name)
630-
}
631-
}
632-
} else if method_name == "next" && match_trait_method(cx, arg, &paths::ITERATOR) {
633-
iter_next_loop::check(cx, expr);
634-
next_loop_linted = true;
615+
},
616+
"next" => {
617+
next_loop_linted = iter_next_loop::check(cx, arg, expr);
618+
},
619+
_ => {},
635620
}
636621
}
637622
}
623+
638624
if !next_loop_linted {
639625
for_loops_over_fallibles::check(cx, pat, arg);
640626
}
641627
}
642-
643-
/// Returns `true` if the type of expr is one that provides `IntoIterator` impls
644-
/// for `&T` and `&mut T`, such as `Vec`.
645-
#[rustfmt::skip]
646-
fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
647-
// no walk_ptrs_ty: calling iter() on a reference can make sense because it
648-
// will allow further borrows afterwards
649-
let ty = cx.typeck_results().expr_ty(e);
650-
is_iterable_array(ty, cx) ||
651-
is_type_diagnostic_item(cx, ty, sym::vec_type) ||
652-
match_type(cx, ty, &paths::LINKED_LIST) ||
653-
is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) ||
654-
is_type_diagnostic_item(cx, ty, sym!(hashset_type)) ||
655-
is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) ||
656-
match_type(cx, ty, &paths::BINARY_HEAP) ||
657-
match_type(cx, ty, &paths::BTREEMAP) ||
658-
match_type(cx, ty, &paths::BTREESET)
659-
}
660-
661-
fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool {
662-
// IntoIterator is currently only implemented for array sizes <= 32 in rustc
663-
match ty.kind() {
664-
ty::Array(_, n) => n
665-
.try_eval_usize(cx.tcx, cx.param_env)
666-
.map_or(false, |val| (0..=32).contains(&val)),
667-
_ => false,
668-
}
669-
}

0 commit comments

Comments
 (0)