mirror of
https://github.com/rust-lang/rust-clippy
synced 2025-02-17 14:38:46 +00:00
Simplify check_for_loop_arg
This commit is contained in:
parent
eaf63d6df7
commit
74bd806b05
4 changed files with 90 additions and 67 deletions
|
@ -3,10 +3,17 @@ use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::Expr;
|
use rustc_hir::Expr;
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
|
use rustc_middle::ty::TyS;
|
||||||
|
|
||||||
|
pub(super) fn check(cx: &LateContext<'_>, args: &'hir [Expr<'hir>], arg: &Expr<'_>) {
|
||||||
|
let receiver_ty = cx.typeck_results().expr_ty(&args[0]);
|
||||||
|
let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]);
|
||||||
|
if !TyS::same_type(receiver_ty, receiver_ty_adjusted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn check(cx: &LateContext<'_>, method_args: &'hir [Expr<'hir>], arg: &Expr<'_>) {
|
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
let object = snippet_with_applicability(cx, method_args[0].span, "_", &mut applicability);
|
let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
EXPLICIT_INTO_ITER_LOOP,
|
EXPLICIT_INTO_ITER_LOOP,
|
||||||
|
|
|
@ -1,10 +1,35 @@
|
||||||
use super::EXPLICIT_ITER_LOOP;
|
use super::EXPLICIT_ITER_LOOP;
|
||||||
use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
|
use crate::utils::{match_trait_method, snippet_with_applicability, span_lint_and_sugg};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::Expr;
|
use rustc_hir::{Expr, Mutability};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
|
use rustc_middle::ty::{self, Ty, TyS};
|
||||||
|
use rustc_span::symbol::sym;
|
||||||
|
|
||||||
|
use crate::utils::{is_type_diagnostic_item, match_type, paths};
|
||||||
|
|
||||||
pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) {
|
pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) {
|
||||||
|
let should_lint = match method_name {
|
||||||
|
"iter" | "iter_mut" => is_ref_iterable_type(cx, &args[0]),
|
||||||
|
"into_iter" if match_trait_method(cx, arg, &paths::INTO_ITERATOR) => {
|
||||||
|
let receiver_ty = cx.typeck_results().expr_ty(&args[0]);
|
||||||
|
let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]);
|
||||||
|
let ref_receiver_ty = cx.tcx.mk_ref(
|
||||||
|
cx.tcx.lifetimes.re_erased,
|
||||||
|
ty::TypeAndMut {
|
||||||
|
ty: receiver_ty,
|
||||||
|
mutbl: Mutability::Not,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
TyS::same_type(receiver_ty_adjusted, ref_receiver_ty)
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if !should_lint {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
|
let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
|
||||||
let muta = if method_name == "iter_mut" { "mut " } else { "" };
|
let muta = if method_name == "iter_mut" { "mut " } else { "" };
|
||||||
|
@ -19,3 +44,31 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, met
|
||||||
applicability,
|
applicability,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the type of expr is one that provides `IntoIterator` impls
|
||||||
|
/// for `&T` and `&mut T`, such as `Vec`.
|
||||||
|
#[rustfmt::skip]
|
||||||
|
fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||||
|
// no walk_ptrs_ty: calling iter() on a reference can make sense because it
|
||||||
|
// will allow further borrows afterwards
|
||||||
|
let ty = cx.typeck_results().expr_ty(e);
|
||||||
|
is_iterable_array(ty, cx) ||
|
||||||
|
is_type_diagnostic_item(cx, ty, sym::vec_type) ||
|
||||||
|
match_type(cx, ty, &paths::LINKED_LIST) ||
|
||||||
|
is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) ||
|
||||||
|
is_type_diagnostic_item(cx, ty, sym!(hashset_type)) ||
|
||||||
|
is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) ||
|
||||||
|
match_type(cx, ty, &paths::BINARY_HEAP) ||
|
||||||
|
match_type(cx, ty, &paths::BTREEMAP) ||
|
||||||
|
match_type(cx, ty, &paths::BTREESET)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool {
|
||||||
|
// IntoIterator is currently only implemented for array sizes <= 32 in rustc
|
||||||
|
match ty.kind() {
|
||||||
|
ty::Array(_, n) => n
|
||||||
|
.try_eval_usize(cx.tcx, cx.param_env)
|
||||||
|
.map_or(false, |val| (0..=32).contains(&val)),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
use super::ITER_NEXT_LOOP;
|
use super::ITER_NEXT_LOOP;
|
||||||
use crate::utils::span_lint;
|
use crate::utils::{match_trait_method, paths, span_lint};
|
||||||
use rustc_hir::Expr;
|
use rustc_hir::Expr;
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
|
|
||||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, expr: &Expr<'_>) -> bool {
|
||||||
span_lint(
|
if match_trait_method(cx, arg, &paths::ITERATOR) {
|
||||||
cx,
|
span_lint(
|
||||||
ITER_NEXT_LOOP,
|
cx,
|
||||||
expr.span,
|
ITER_NEXT_LOOP,
|
||||||
"you are iterating over `Iterator::next()` which is an Option; this will compile but is \
|
expr.span,
|
||||||
probably not what you want",
|
"you are iterating over `Iterator::next()` which is an Option; this will compile but is \
|
||||||
);
|
probably not what you want",
|
||||||
|
);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,11 @@ mod while_immutable_condition;
|
||||||
mod while_let_loop;
|
mod while_let_loop;
|
||||||
mod while_let_on_iterator;
|
mod while_let_on_iterator;
|
||||||
|
|
||||||
use crate::utils::{higher, is_type_diagnostic_item, match_trait_method, match_type, paths};
|
use crate::utils::higher;
|
||||||
use rustc_hir::{Expr, ExprKind, LoopSource, Mutability, Pat};
|
use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::{self, Ty, TyS};
|
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
use rustc_span::source_map::Span;
|
use rustc_span::source_map::Span;
|
||||||
use rustc_span::symbol::sym;
|
|
||||||
use utils::{get_span_of_entire_for_loop, make_iterator_snippet, IncrementVisitor, InitializeVisitor};
|
use utils::{get_span_of_entire_for_loop, make_iterator_snippet, IncrementVisitor, InitializeVisitor};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
|
@ -603,67 +601,27 @@ fn check_for_loop<'tcx>(
|
||||||
|
|
||||||
fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: &Expr<'_>) {
|
fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: &Expr<'_>) {
|
||||||
let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used
|
let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used
|
||||||
|
|
||||||
if let ExprKind::MethodCall(ref method, _, ref args, _) = arg.kind {
|
if let ExprKind::MethodCall(ref method, _, ref args, _) = arg.kind {
|
||||||
// just the receiver, no arguments
|
// just the receiver, no arguments
|
||||||
if args.len() == 1 {
|
if args.len() == 1 {
|
||||||
let method_name = &*method.ident.as_str();
|
let method_name = &*method.ident.as_str();
|
||||||
// check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
|
// check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
|
||||||
if method_name == "iter" || method_name == "iter_mut" {
|
match method_name {
|
||||||
if is_ref_iterable_type(cx, &args[0]) {
|
"iter" | "iter_mut" => explicit_iter_loop::check(cx, args, arg, method_name),
|
||||||
|
"into_iter" => {
|
||||||
explicit_iter_loop::check(cx, args, arg, method_name);
|
explicit_iter_loop::check(cx, args, arg, method_name);
|
||||||
}
|
|
||||||
} else if method_name == "into_iter" && match_trait_method(cx, arg, &paths::INTO_ITERATOR) {
|
|
||||||
let receiver_ty = cx.typeck_results().expr_ty(&args[0]);
|
|
||||||
let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]);
|
|
||||||
if TyS::same_type(receiver_ty, receiver_ty_adjusted) {
|
|
||||||
explicit_into_iter_loop::check(cx, args, arg);
|
explicit_into_iter_loop::check(cx, args, arg);
|
||||||
} else {
|
},
|
||||||
let ref_receiver_ty = cx.tcx.mk_ref(
|
"next" => {
|
||||||
cx.tcx.lifetimes.re_erased,
|
next_loop_linted = iter_next_loop::check(cx, arg, expr);
|
||||||
ty::TypeAndMut {
|
},
|
||||||
ty: receiver_ty,
|
_ => {},
|
||||||
mutbl: Mutability::Not,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if TyS::same_type(receiver_ty_adjusted, ref_receiver_ty) {
|
|
||||||
explicit_iter_loop::check(cx, args, arg, method_name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if method_name == "next" && match_trait_method(cx, arg, &paths::ITERATOR) {
|
|
||||||
iter_next_loop::check(cx, expr);
|
|
||||||
next_loop_linted = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !next_loop_linted {
|
if !next_loop_linted {
|
||||||
for_loops_over_fallibles::check(cx, pat, arg);
|
for_loops_over_fallibles::check(cx, pat, arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the type of expr is one that provides `IntoIterator` impls
|
|
||||||
/// for `&T` and `&mut T`, such as `Vec`.
|
|
||||||
#[rustfmt::skip]
|
|
||||||
fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
|
||||||
// no walk_ptrs_ty: calling iter() on a reference can make sense because it
|
|
||||||
// will allow further borrows afterwards
|
|
||||||
let ty = cx.typeck_results().expr_ty(e);
|
|
||||||
is_iterable_array(ty, cx) ||
|
|
||||||
is_type_diagnostic_item(cx, ty, sym::vec_type) ||
|
|
||||||
match_type(cx, ty, &paths::LINKED_LIST) ||
|
|
||||||
is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) ||
|
|
||||||
is_type_diagnostic_item(cx, ty, sym!(hashset_type)) ||
|
|
||||||
is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) ||
|
|
||||||
match_type(cx, ty, &paths::BINARY_HEAP) ||
|
|
||||||
match_type(cx, ty, &paths::BTREEMAP) ||
|
|
||||||
match_type(cx, ty, &paths::BTREESET)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool {
|
|
||||||
// IntoIterator is currently only implemented for array sizes <= 32 in rustc
|
|
||||||
match ty.kind() {
|
|
||||||
ty::Array(_, n) => n
|
|
||||||
.try_eval_usize(cx.tcx, cx.param_env)
|
|
||||||
.map_or(false, |val| (0..=32).contains(&val)),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue