mirror of
https://github.com/rust-lang/rust-clippy
synced 2025-02-25 11:57:25 +00:00
Improvements to while_let_on_iterator
* Suggest `&mut iter` when the iterator is used after the loop. * Suggest `&mut iter` when the iterator is a field in a struct. * Don't lint when the iterator is a field in a struct, and the struct is used in the loop. * Lint when the loop is nested in another loop, but suggest `&mut iter` unless the iterator is from a local declared inside the loop.
This commit is contained in:
parent
aa15a5442a
commit
daca50a515
8 changed files with 721 additions and 212 deletions
|
@ -383,7 +383,7 @@ pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span:
|
||||||
let mut no_stars = String::with_capacity(doc.len());
|
let mut no_stars = String::with_capacity(doc.len());
|
||||||
for line in doc.lines() {
|
for line in doc.lines() {
|
||||||
let mut chars = line.chars();
|
let mut chars = line.chars();
|
||||||
while let Some(c) = chars.next() {
|
for c in &mut chars {
|
||||||
if c.is_whitespace() {
|
if c.is_whitespace() {
|
||||||
no_stars.push(c);
|
no_stars.push(c);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,170 +1,353 @@
|
||||||
use super::utils::{LoopNestVisitor, Nesting};
|
|
||||||
use super::WHILE_LET_ON_ITERATOR;
|
use super::WHILE_LET_ON_ITERATOR;
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
use clippy_utils::source::snippet_with_applicability;
|
||||||
use clippy_utils::ty::implements_trait;
|
use clippy_utils::{get_enclosing_loop, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used};
|
||||||
use clippy_utils::usage::mutated_variables;
|
|
||||||
use clippy_utils::{
|
|
||||||
get_enclosing_block, is_refutable, is_trait_method, last_path_segment, path_to_local, path_to_local_id,
|
|
||||||
};
|
|
||||||
use if_chain::if_chain;
|
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor};
|
use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
|
||||||
use rustc_hir::{Expr, ExprKind, HirId, MatchSource, Node, PatKind};
|
use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, MatchSource, Node, PatKind, QPath, UnOp};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::hir::map::Map;
|
use rustc_span::{symbol::sym, Span, Symbol};
|
||||||
use rustc_span::symbol::sym;
|
|
||||||
|
|
||||||
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||||
if let ExprKind::Match(match_expr, arms, MatchSource::WhileLetDesugar) = expr.kind {
|
if let ExprKind::Match(scrutinee_expr, [arm, _], MatchSource::WhileLetDesugar) = expr.kind {
|
||||||
let pat = &arms[0].pat.kind;
|
let some_pat = match arm.pat.kind {
|
||||||
if let (&PatKind::TupleStruct(ref qpath, pat_args, _), &ExprKind::MethodCall(method_path, _, method_args, _)) =
|
PatKind::TupleStruct(QPath::Resolved(None, path), sub_pats, _) => match path.res {
|
||||||
(pat, &match_expr.kind)
|
Res::Def(_, id) if match_def_path(cx, id, &paths::OPTION_SOME) => sub_pats.first(),
|
||||||
{
|
_ => return,
|
||||||
let iter_expr = &method_args[0];
|
},
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
// Don't lint when the iterator is recreated on every iteration
|
let iter_expr = match scrutinee_expr.kind {
|
||||||
if_chain! {
|
ExprKind::MethodCall(name, _, [iter_expr], _)
|
||||||
if let ExprKind::MethodCall(..) | ExprKind::Call(..) = iter_expr.kind;
|
if name.ident.name == sym::next && is_trait_method(cx, scrutinee_expr, sym::Iterator) =>
|
||||||
if let Some(iter_def_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
|
{
|
||||||
if implements_trait(cx, cx.typeck_results().expr_ty(iter_expr), iter_def_id, &[]);
|
if let Some(iter_expr) = try_parse_iter_expr(cx, iter_expr) {
|
||||||
then {
|
iter_expr
|
||||||
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
let lhs_constructor = last_path_segment(qpath);
|
// Needed to find an outer loop, if there are any.
|
||||||
if method_path.ident.name == sym::next
|
let loop_expr = if let Some((_, Node::Expr(e))) = cx.tcx.hir().parent_iter(expr.hir_id).nth(1) {
|
||||||
&& is_trait_method(cx, match_expr, sym::Iterator)
|
e
|
||||||
&& lhs_constructor.ident.name == sym::Some
|
} else {
|
||||||
&& (pat_args.is_empty()
|
return;
|
||||||
|| !is_refutable(cx, pat_args[0])
|
};
|
||||||
&& !is_used_inside(cx, iter_expr, arms[0].body)
|
|
||||||
&& !is_iterator_used_after_while_let(cx, iter_expr)
|
// Refutable patterns don't work with for loops.
|
||||||
&& !is_nested(cx, expr, &method_args[0]))
|
// The iterator also can't be accessed withing the loop.
|
||||||
{
|
if some_pat.map_or(true, |p| is_refutable(cx, p)) || uses_iter(cx, &iter_expr, arm.body) {
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
return;
|
||||||
let iterator = snippet_with_applicability(cx, method_args[0].span, "_", &mut applicability);
|
|
||||||
let loop_var = if pat_args.is_empty() {
|
|
||||||
"_".to_string()
|
|
||||||
} else {
|
|
||||||
snippet_with_applicability(cx, pat_args[0].span, "_", &mut applicability).into_owned()
|
|
||||||
};
|
|
||||||
span_lint_and_sugg(
|
|
||||||
cx,
|
|
||||||
WHILE_LET_ON_ITERATOR,
|
|
||||||
expr.span.with_hi(match_expr.span.hi()),
|
|
||||||
"this loop could be written as a `for` loop",
|
|
||||||
"try",
|
|
||||||
format!("for {} in {}", loop_var, iterator),
|
|
||||||
applicability,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the iterator is a field or the iterator is accessed after the loop is complete it needs to be
|
||||||
|
// borrowed mutably.
|
||||||
|
// TODO: If the struct can be partially moved from and the struct isn't used afterwards a mutable
|
||||||
|
// borrow of a field isn't necessary.
|
||||||
|
let ref_mut = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) {
|
||||||
|
"&mut "
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
|
let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability);
|
||||||
|
let loop_var = some_pat.map_or_else(
|
||||||
|
|| "_".into(),
|
||||||
|
|pat| snippet_with_applicability(cx, pat.span, "_", &mut applicability).into_owned(),
|
||||||
|
);
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
WHILE_LET_ON_ITERATOR,
|
||||||
|
expr.span.with_hi(scrutinee_expr.span.hi()),
|
||||||
|
"this loop could be written as a `for` loop",
|
||||||
|
"try",
|
||||||
|
format!("for {} in {}{}", loop_var, ref_mut, iterator),
|
||||||
|
applicability,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_used_inside<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, container: &'tcx Expr<'_>) -> bool {
|
#[derive(Debug)]
|
||||||
let def_id = match path_to_local(expr) {
|
struct IterExpr {
|
||||||
Some(id) => id,
|
/// The span of the whole expression, not just the path and fields stored here.
|
||||||
None => return false,
|
span: Span,
|
||||||
};
|
/// The fields used, in order of child to parent.
|
||||||
if let Some(used_mutably) = mutated_variables(container, cx) {
|
fields: Vec<Symbol>,
|
||||||
if used_mutably.contains(&def_id) {
|
/// The path being used.
|
||||||
return true;
|
path: Res,
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
/// Parses any expression to find out which field of which variable is used. Will return `None` if
|
||||||
fn is_iterator_used_after_while_let<'tcx>(cx: &LateContext<'tcx>, iter_expr: &'tcx Expr<'_>) -> bool {
|
/// the expression might have side effects.
|
||||||
let def_id = match path_to_local(iter_expr) {
|
fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> {
|
||||||
Some(id) => id,
|
let span = e.span;
|
||||||
None => return false,
|
let mut fields = Vec::new();
|
||||||
};
|
|
||||||
let mut visitor = VarUsedAfterLoopVisitor {
|
|
||||||
def_id,
|
|
||||||
iter_expr_id: iter_expr.hir_id,
|
|
||||||
past_while_let: false,
|
|
||||||
var_used_after_while_let: false,
|
|
||||||
};
|
|
||||||
if let Some(enclosing_block) = get_enclosing_block(cx, def_id) {
|
|
||||||
walk_block(&mut visitor, enclosing_block);
|
|
||||||
}
|
|
||||||
visitor.var_used_after_while_let
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_nested(cx: &LateContext<'_>, match_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool {
|
|
||||||
if_chain! {
|
|
||||||
if let Some(loop_block) = get_enclosing_block(cx, match_expr.hir_id);
|
|
||||||
let parent_node = cx.tcx.hir().get_parent_node(loop_block.hir_id);
|
|
||||||
if let Some(Node::Expr(loop_expr)) = cx.tcx.hir().find(parent_node);
|
|
||||||
then {
|
|
||||||
return is_loop_nested(cx, loop_expr, iter_expr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_loop_nested(cx: &LateContext<'_>, loop_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool {
|
|
||||||
let mut id = loop_expr.hir_id;
|
|
||||||
let iter_id = if let Some(id) = path_to_local(iter_expr) {
|
|
||||||
id
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
loop {
|
loop {
|
||||||
let parent = cx.tcx.hir().get_parent_node(id);
|
match e.kind {
|
||||||
if parent == id {
|
ExprKind::Path(ref path) => {
|
||||||
return false;
|
break Some(IterExpr {
|
||||||
|
span,
|
||||||
|
fields,
|
||||||
|
path: cx.qpath_res(path, e.hir_id),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
ExprKind::Field(base, name) => {
|
||||||
|
fields.push(name.name);
|
||||||
|
e = base;
|
||||||
|
},
|
||||||
|
// Dereferencing a pointer has no side effects and doesn't affect which field is being used.
|
||||||
|
ExprKind::Unary(UnOp::Deref, base) if cx.typeck_results().expr_ty(base).is_ref() => e = base,
|
||||||
|
|
||||||
|
// Shouldn't have side effects, but there's no way to trace which field is used. So forget which fields have
|
||||||
|
// already been seen.
|
||||||
|
ExprKind::Index(base, idx) if !idx.can_have_side_effects() => {
|
||||||
|
fields.clear();
|
||||||
|
e = base;
|
||||||
|
},
|
||||||
|
ExprKind::Unary(UnOp::Deref, base) => {
|
||||||
|
fields.clear();
|
||||||
|
e = base;
|
||||||
|
},
|
||||||
|
|
||||||
|
// No effect and doesn't affect which field is being used.
|
||||||
|
ExprKind::DropTemps(base) | ExprKind::AddrOf(_, _, base) | ExprKind::Type(base, _) => e = base,
|
||||||
|
_ => break None,
|
||||||
}
|
}
|
||||||
match cx.tcx.hir().find(parent) {
|
}
|
||||||
Some(Node::Expr(expr)) => {
|
}
|
||||||
if let ExprKind::Loop(..) = expr.kind {
|
|
||||||
|
fn is_expr_same_field(cx: &LateContext<'_>, mut e: &Expr<'_>, mut fields: &[Symbol], path_res: Res) -> bool {
|
||||||
|
loop {
|
||||||
|
match (&e.kind, fields) {
|
||||||
|
(&ExprKind::Field(base, name), [head_field, tail_fields @ ..]) if name.name == *head_field => {
|
||||||
|
e = base;
|
||||||
|
fields = tail_fields;
|
||||||
|
},
|
||||||
|
(ExprKind::Path(path), []) => {
|
||||||
|
break cx.qpath_res(path, e.hir_id) == path_res;
|
||||||
|
},
|
||||||
|
(&(ExprKind::DropTemps(base) | ExprKind::AddrOf(_, _, base) | ExprKind::Type(base, _)), _) => e = base,
|
||||||
|
_ => break false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the given expression is the same field as, is a child of, of the parent of the given
|
||||||
|
/// field. Used to check if the expression can be used while the given field is borrowed.
|
||||||
|
fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fields: &[Symbol], path_res: Res) -> bool {
|
||||||
|
match expr.kind {
|
||||||
|
ExprKind::Field(base, name) => {
|
||||||
|
if let Some((head_field, tail_fields)) = fields.split_first() {
|
||||||
|
if name.name == *head_field && is_expr_same_field(cx, base, fields, path_res) {
|
||||||
return true;
|
return true;
|
||||||
};
|
|
||||||
},
|
|
||||||
Some(Node::Block(block)) => {
|
|
||||||
let mut block_visitor = LoopNestVisitor {
|
|
||||||
hir_id: id,
|
|
||||||
iterator: iter_id,
|
|
||||||
nesting: Nesting::Unknown,
|
|
||||||
};
|
|
||||||
walk_block(&mut block_visitor, block);
|
|
||||||
if block_visitor.nesting == Nesting::RuledOut {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
},
|
// Check if the expression is a parent field
|
||||||
Some(Node::Stmt(_)) => (),
|
let mut fields_iter = tail_fields.iter();
|
||||||
_ => {
|
while let Some(field) = fields_iter.next() {
|
||||||
return false;
|
if *field == name.name && is_expr_same_field(cx, base, fields_iter.as_slice(), path_res) {
|
||||||
},
|
return true;
|
||||||
}
|
}
|
||||||
id = parent;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct VarUsedAfterLoopVisitor {
|
|
||||||
def_id: HirId,
|
|
||||||
iter_expr_id: HirId,
|
|
||||||
past_while_let: bool,
|
|
||||||
var_used_after_while_let: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'tcx> Visitor<'tcx> for VarUsedAfterLoopVisitor {
|
|
||||||
type Map = Map<'tcx>;
|
|
||||||
|
|
||||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
|
||||||
if self.past_while_let {
|
|
||||||
if path_to_local_id(expr, self.def_id) {
|
|
||||||
self.var_used_after_while_let = true;
|
|
||||||
}
|
}
|
||||||
} else if self.iter_expr_id == expr.hir_id {
|
|
||||||
self.past_while_let = true;
|
// Check if the expression is a child field.
|
||||||
}
|
let mut e = base;
|
||||||
walk_expr(self, expr);
|
loop {
|
||||||
}
|
match e.kind {
|
||||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
ExprKind::Field(..) if is_expr_same_field(cx, e, fields, path_res) => break true,
|
||||||
NestedVisitorMap::None
|
ExprKind::Field(base, _) | ExprKind::DropTemps(base) | ExprKind::Type(base, _) => e = base,
|
||||||
|
ExprKind::Path(ref path) if fields.is_empty() => {
|
||||||
|
break cx.qpath_res(path, e.hir_id) == path_res;
|
||||||
|
},
|
||||||
|
_ => break false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// If the path matches, this is either an exact match, of the expression is a parent of the field.
|
||||||
|
ExprKind::Path(ref path) => cx.qpath_res(path, expr.hir_id) == path_res,
|
||||||
|
ExprKind::DropTemps(base) | ExprKind::Type(base, _) | ExprKind::AddrOf(_, _, base) => {
|
||||||
|
is_expr_same_child_or_parent_field(cx, base, fields, path_res)
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Strips off all field and path expressions. Used to skip them after failing to check for
|
||||||
|
/// equality.
|
||||||
|
fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) {
|
||||||
|
let mut e = expr;
|
||||||
|
let e = loop {
|
||||||
|
match e.kind {
|
||||||
|
ExprKind::Field(base, _) | ExprKind::DropTemps(base) | ExprKind::Type(base, _) => e = base,
|
||||||
|
ExprKind::Path(_) => return (None, true),
|
||||||
|
_ => break e,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(Some(e), e.hir_id != expr.hir_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the given expression uses the iterator.
|
||||||
|
fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool {
|
||||||
|
struct V<'a, 'b, 'tcx> {
|
||||||
|
cx: &'a LateContext<'tcx>,
|
||||||
|
iter_expr: &'b IterExpr,
|
||||||
|
uses_iter: bool,
|
||||||
|
}
|
||||||
|
impl Visitor<'tcx> for V<'_, '_, 'tcx> {
|
||||||
|
type Map = ErasedMap<'tcx>;
|
||||||
|
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||||
|
NestedVisitorMap::None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
|
||||||
|
if self.uses_iter {
|
||||||
|
// return
|
||||||
|
} else if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) {
|
||||||
|
self.uses_iter = true;
|
||||||
|
} else if let (e, true) = skip_fields_and_path(e) {
|
||||||
|
if let Some(e) = e {
|
||||||
|
self.visit_expr(e);
|
||||||
|
}
|
||||||
|
} else if let ExprKind::Closure(_, _, id, _, _) = e.kind {
|
||||||
|
if is_res_used(self.cx, self.iter_expr.path, id) {
|
||||||
|
self.uses_iter = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
walk_expr(self, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut v = V {
|
||||||
|
cx,
|
||||||
|
iter_expr,
|
||||||
|
uses_iter: false,
|
||||||
|
};
|
||||||
|
v.visit_expr(container);
|
||||||
|
v.uses_iter
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
|
fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: &'tcx Expr<'_>) -> bool {
|
||||||
|
struct AfterLoopVisitor<'a, 'b, 'tcx> {
|
||||||
|
cx: &'a LateContext<'tcx>,
|
||||||
|
iter_expr: &'b IterExpr,
|
||||||
|
loop_id: HirId,
|
||||||
|
after_loop: bool,
|
||||||
|
used_iter: bool,
|
||||||
|
}
|
||||||
|
impl Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> {
|
||||||
|
type Map = ErasedMap<'tcx>;
|
||||||
|
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||||
|
NestedVisitorMap::None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
|
||||||
|
if self.used_iter {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if self.after_loop {
|
||||||
|
if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) {
|
||||||
|
self.used_iter = true;
|
||||||
|
} else if let (e, true) = skip_fields_and_path(e) {
|
||||||
|
if let Some(e) = e {
|
||||||
|
self.visit_expr(e);
|
||||||
|
}
|
||||||
|
} else if let ExprKind::Closure(_, _, id, _, _) = e.kind {
|
||||||
|
self.used_iter |= is_res_used(self.cx, self.iter_expr.path, id);
|
||||||
|
} else {
|
||||||
|
walk_expr(self, e);
|
||||||
|
}
|
||||||
|
} else if self.loop_id == e.hir_id {
|
||||||
|
self.after_loop = true;
|
||||||
|
} else {
|
||||||
|
walk_expr(self, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NestedLoopVisitor<'a, 'b, 'tcx> {
|
||||||
|
cx: &'a LateContext<'tcx>,
|
||||||
|
iter_expr: &'b IterExpr,
|
||||||
|
local_id: HirId,
|
||||||
|
loop_id: HirId,
|
||||||
|
after_loop: bool,
|
||||||
|
found_local: bool,
|
||||||
|
used_after: bool,
|
||||||
|
}
|
||||||
|
impl Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> {
|
||||||
|
type Map = ErasedMap<'tcx>;
|
||||||
|
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||||
|
NestedVisitorMap::None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_local(&mut self, l: &'tcx Local<'_>) {
|
||||||
|
if !self.after_loop {
|
||||||
|
l.pat.each_binding_or_first(&mut |_, id, _, _| {
|
||||||
|
if id == self.local_id {
|
||||||
|
self.found_local = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(e) = l.init {
|
||||||
|
self.visit_expr(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
|
||||||
|
if self.used_after {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if self.after_loop {
|
||||||
|
if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) {
|
||||||
|
self.used_after = true;
|
||||||
|
} else if let (e, true) = skip_fields_and_path(e) {
|
||||||
|
if let Some(e) = e {
|
||||||
|
self.visit_expr(e);
|
||||||
|
}
|
||||||
|
} else if let ExprKind::Closure(_, _, id, _, _) = e.kind {
|
||||||
|
self.used_after |= is_res_used(self.cx, self.iter_expr.path, id);
|
||||||
|
} else {
|
||||||
|
walk_expr(self, e);
|
||||||
|
}
|
||||||
|
} else if e.hir_id == self.loop_id {
|
||||||
|
self.after_loop = true;
|
||||||
|
} else {
|
||||||
|
walk_expr(self, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(e) = get_enclosing_loop(cx.tcx, loop_expr) {
|
||||||
|
// The iterator expression will be used on the next iteration unless it is declared within the outer
|
||||||
|
// loop.
|
||||||
|
let local_id = match iter_expr.path {
|
||||||
|
Res::Local(id) => id,
|
||||||
|
_ => return true,
|
||||||
|
};
|
||||||
|
let mut v = NestedLoopVisitor {
|
||||||
|
cx,
|
||||||
|
iter_expr,
|
||||||
|
local_id,
|
||||||
|
loop_id: loop_expr.hir_id,
|
||||||
|
after_loop: false,
|
||||||
|
found_local: false,
|
||||||
|
used_after: false,
|
||||||
|
};
|
||||||
|
v.visit_expr(e);
|
||||||
|
v.used_after || !v.found_local
|
||||||
|
} else {
|
||||||
|
let mut v = AfterLoopVisitor {
|
||||||
|
cx,
|
||||||
|
iter_expr,
|
||||||
|
loop_id: loop_expr.hir_id,
|
||||||
|
after_loop: false,
|
||||||
|
used_iter: false,
|
||||||
|
};
|
||||||
|
v.visit_expr(&cx.tcx.hir().body(cx.enclosing_body.unwrap()).value);
|
||||||
|
v.used_iter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -855,6 +855,24 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the loop enclosing the given expression, if any.
|
||||||
|
pub fn get_enclosing_loop(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||||
|
let map = tcx.hir();
|
||||||
|
for (_, node) in map.parent_iter(expr.hir_id) {
|
||||||
|
match node {
|
||||||
|
Node::Expr(
|
||||||
|
e @ Expr {
|
||||||
|
kind: ExprKind::Loop(..),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
) => return Some(e),
|
||||||
|
Node::Expr(_) | Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the parent node if it's an impl block.
|
/// Gets the parent node if it's an impl block.
|
||||||
pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
|
pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
|
||||||
let map = tcx.hir();
|
let map = tcx.hir();
|
||||||
|
|
|
@ -289,7 +289,7 @@ fn has_enclosing_paren(sugg: impl AsRef<str>) -> bool {
|
||||||
let mut chars = sugg.as_ref().chars();
|
let mut chars = sugg.as_ref().chars();
|
||||||
if let Some('(') = chars.next() {
|
if let Some('(') = chars.next() {
|
||||||
let mut depth = 1;
|
let mut depth = 1;
|
||||||
while let Some(c) = chars.next() {
|
for c in &mut chars {
|
||||||
if c == '(' {
|
if c == '(' {
|
||||||
depth += 1;
|
depth += 1;
|
||||||
} else if c == ')' {
|
} else if c == ')' {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::path_to_local_id;
|
use crate::path_to_local_id;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor};
|
use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor};
|
||||||
use rustc_hir::{Arm, Block, Body, Destination, Expr, ExprKind, HirId, Stmt};
|
use rustc_hir::{def::Res, Arm, Block, Body, BodyId, Destination, Expr, ExprKind, HirId, Stmt};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::hir::map::Map;
|
use rustc_middle::hir::map::Map;
|
||||||
|
|
||||||
|
@ -218,6 +218,7 @@ impl<'tcx> Visitable<'tcx> for &'tcx Arm<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calls the given function for each break expression.
|
||||||
pub fn visit_break_exprs<'tcx>(
|
pub fn visit_break_exprs<'tcx>(
|
||||||
node: impl Visitable<'tcx>,
|
node: impl Visitable<'tcx>,
|
||||||
f: impl FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>),
|
f: impl FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>),
|
||||||
|
@ -239,3 +240,36 @@ pub fn visit_break_exprs<'tcx>(
|
||||||
|
|
||||||
node.visit(&mut V(f));
|
node.visit(&mut V(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if the given resolved path is used the body.
|
||||||
|
pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
|
||||||
|
struct V<'a, 'tcx> {
|
||||||
|
cx: &'a LateContext<'tcx>,
|
||||||
|
res: Res,
|
||||||
|
found: bool,
|
||||||
|
}
|
||||||
|
impl Visitor<'tcx> for V<'_, 'tcx> {
|
||||||
|
type Map = Map<'tcx>;
|
||||||
|
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||||
|
NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
|
||||||
|
if self.found {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let ExprKind::Path(p) = &e.kind {
|
||||||
|
if self.cx.qpath_res(p, e.hir_id) == self.res {
|
||||||
|
self.found = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
walk_expr(self, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut v = V { cx, res, found: false };
|
||||||
|
v.visit_expr(&cx.tcx.hir().body(body).value);
|
||||||
|
v.found
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// run-rustfix
|
// run-rustfix
|
||||||
|
|
||||||
#![warn(clippy::while_let_on_iterator)]
|
#![warn(clippy::while_let_on_iterator)]
|
||||||
#![allow(clippy::never_loop, unreachable_code, unused_mut)]
|
#![allow(clippy::never_loop, unreachable_code, unused_mut, dead_code)]
|
||||||
|
|
||||||
fn base() {
|
fn base() {
|
||||||
let mut iter = 1..20;
|
let mut iter = 1..20;
|
||||||
|
@ -38,13 +38,6 @@ fn base() {
|
||||||
println!("next: {:?}", iter.next());
|
println!("next: {:?}", iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
// or this
|
|
||||||
let mut iter = 1u32..20;
|
|
||||||
while let Some(_) = iter.next() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
println!("Remaining iter {:?}", iter);
|
|
||||||
|
|
||||||
// or this
|
// or this
|
||||||
let mut iter = 1u32..20;
|
let mut iter = 1u32..20;
|
||||||
while let Some(_) = iter.next() {
|
while let Some(_) = iter.next() {
|
||||||
|
@ -135,18 +128,6 @@ fn refutable2() {
|
||||||
|
|
||||||
fn nested_loops() {
|
fn nested_loops() {
|
||||||
let a = [42, 1337];
|
let a = [42, 1337];
|
||||||
let mut y = a.iter();
|
|
||||||
loop {
|
|
||||||
// x is reused, so don't lint here
|
|
||||||
while let Some(_) = y.next() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut y = a.iter();
|
|
||||||
for _ in 0..2 {
|
|
||||||
while let Some(_) = y.next() {
|
|
||||||
// y is reused, don't lint
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut y = a.iter();
|
let mut y = a.iter();
|
||||||
|
@ -205,13 +186,138 @@ fn issue1654() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn issue6491() {
|
||||||
base();
|
// Used in outer loop, needs &mut
|
||||||
refutable();
|
let mut it = 1..40;
|
||||||
refutable2();
|
while let Some(n) = it.next() {
|
||||||
nested_loops();
|
for m in &mut it {
|
||||||
issue1121();
|
if m % 10 == 0 {
|
||||||
issue2965();
|
break;
|
||||||
issue3670();
|
}
|
||||||
issue1654();
|
println!("doing something with m: {}", m);
|
||||||
|
}
|
||||||
|
println!("n still is {}", n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is fine, inner loop uses a new iterator.
|
||||||
|
let mut it = 1..40;
|
||||||
|
for n in it {
|
||||||
|
let mut it = 1..40;
|
||||||
|
for m in it {
|
||||||
|
if m % 10 == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
println!("doing something with m: {}", m);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Weird binding shouldn't change anything.
|
||||||
|
let (mut it, _) = (1..40, 0);
|
||||||
|
for m in it {
|
||||||
|
if m % 10 == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
println!("doing something with m: {}", m);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used after the loop, needs &mut.
|
||||||
|
let mut it = 1..40;
|
||||||
|
for m in &mut it {
|
||||||
|
if m % 10 == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
println!("doing something with m: {}", m);
|
||||||
|
}
|
||||||
|
println!("next item {}", it.next().unwrap());
|
||||||
|
|
||||||
|
println!("n still is {}", n);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn issue6231() {
|
||||||
|
// Closure in the outer loop, needs &mut
|
||||||
|
let mut it = 1..40;
|
||||||
|
let mut opt = Some(0);
|
||||||
|
while let Some(n) = opt.take().or_else(|| it.next()) {
|
||||||
|
for m in &mut it {
|
||||||
|
if n % 10 == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
println!("doing something with m: {}", m);
|
||||||
|
}
|
||||||
|
println!("n still is {}", n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn issue1924() {
|
||||||
|
struct S<T>(T);
|
||||||
|
impl<T: Iterator<Item = u32>> S<T> {
|
||||||
|
fn f(&mut self) -> Option<u32> {
|
||||||
|
// Used as a field.
|
||||||
|
for i in &mut self.0 {
|
||||||
|
if !(3..=7).contains(&i) {
|
||||||
|
return Some(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f2(&mut self) -> Option<u32> {
|
||||||
|
// Don't lint, self borrowed inside the loop
|
||||||
|
while let Some(i) = self.0.next() {
|
||||||
|
if i == 1 {
|
||||||
|
return self.f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Iterator<Item = u32>> S<(S<T>, Option<u32>)> {
|
||||||
|
fn f3(&mut self) -> Option<u32> {
|
||||||
|
// Don't lint, self borrowed inside the loop
|
||||||
|
while let Some(i) = self.0.0.0.next() {
|
||||||
|
if i == 1 {
|
||||||
|
return self.0.0.f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while let Some(i) = self.0.0.0.next() {
|
||||||
|
if i == 1 {
|
||||||
|
return self.f3();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This one is fine, a different field is borrowed
|
||||||
|
for i in &mut self.0.0.0 {
|
||||||
|
if i == 1 {
|
||||||
|
return self.0.1.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S2<T>(T, u32);
|
||||||
|
impl<T: Iterator<Item = u32>> Iterator for S2<T> {
|
||||||
|
type Item = u32;
|
||||||
|
fn next(&mut self) -> Option<u32> {
|
||||||
|
self.0.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't lint, field of the iterator is accessed in the loop
|
||||||
|
let mut it = S2(1..40, 0);
|
||||||
|
while let Some(n) = it.next() {
|
||||||
|
if n == it.1 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needs &mut, field of the iterator is accessed after the loop
|
||||||
|
let mut it = S2(1..40, 0);
|
||||||
|
for n in &mut it {
|
||||||
|
if n == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("iterator field {}", it.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// run-rustfix
|
// run-rustfix
|
||||||
|
|
||||||
#![warn(clippy::while_let_on_iterator)]
|
#![warn(clippy::while_let_on_iterator)]
|
||||||
#![allow(clippy::never_loop, unreachable_code, unused_mut)]
|
#![allow(clippy::never_loop, unreachable_code, unused_mut, dead_code)]
|
||||||
|
|
||||||
fn base() {
|
fn base() {
|
||||||
let mut iter = 1..20;
|
let mut iter = 1..20;
|
||||||
|
@ -38,13 +38,6 @@ fn base() {
|
||||||
println!("next: {:?}", iter.next());
|
println!("next: {:?}", iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
// or this
|
|
||||||
let mut iter = 1u32..20;
|
|
||||||
while let Some(_) = iter.next() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
println!("Remaining iter {:?}", iter);
|
|
||||||
|
|
||||||
// or this
|
// or this
|
||||||
let mut iter = 1u32..20;
|
let mut iter = 1u32..20;
|
||||||
while let Some(_) = iter.next() {
|
while let Some(_) = iter.next() {
|
||||||
|
@ -135,18 +128,6 @@ fn refutable2() {
|
||||||
|
|
||||||
fn nested_loops() {
|
fn nested_loops() {
|
||||||
let a = [42, 1337];
|
let a = [42, 1337];
|
||||||
let mut y = a.iter();
|
|
||||||
loop {
|
|
||||||
// x is reused, so don't lint here
|
|
||||||
while let Some(_) = y.next() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut y = a.iter();
|
|
||||||
for _ in 0..2 {
|
|
||||||
while let Some(_) = y.next() {
|
|
||||||
// y is reused, don't lint
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut y = a.iter();
|
let mut y = a.iter();
|
||||||
|
@ -205,13 +186,138 @@ fn issue1654() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn issue6491() {
|
||||||
base();
|
// Used in outer loop, needs &mut
|
||||||
refutable();
|
let mut it = 1..40;
|
||||||
refutable2();
|
while let Some(n) = it.next() {
|
||||||
nested_loops();
|
while let Some(m) = it.next() {
|
||||||
issue1121();
|
if m % 10 == 0 {
|
||||||
issue2965();
|
break;
|
||||||
issue3670();
|
}
|
||||||
issue1654();
|
println!("doing something with m: {}", m);
|
||||||
|
}
|
||||||
|
println!("n still is {}", n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is fine, inner loop uses a new iterator.
|
||||||
|
let mut it = 1..40;
|
||||||
|
while let Some(n) = it.next() {
|
||||||
|
let mut it = 1..40;
|
||||||
|
while let Some(m) = it.next() {
|
||||||
|
if m % 10 == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
println!("doing something with m: {}", m);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Weird binding shouldn't change anything.
|
||||||
|
let (mut it, _) = (1..40, 0);
|
||||||
|
while let Some(m) = it.next() {
|
||||||
|
if m % 10 == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
println!("doing something with m: {}", m);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used after the loop, needs &mut.
|
||||||
|
let mut it = 1..40;
|
||||||
|
while let Some(m) = it.next() {
|
||||||
|
if m % 10 == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
println!("doing something with m: {}", m);
|
||||||
|
}
|
||||||
|
println!("next item {}", it.next().unwrap());
|
||||||
|
|
||||||
|
println!("n still is {}", n);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn issue6231() {
|
||||||
|
// Closure in the outer loop, needs &mut
|
||||||
|
let mut it = 1..40;
|
||||||
|
let mut opt = Some(0);
|
||||||
|
while let Some(n) = opt.take().or_else(|| it.next()) {
|
||||||
|
while let Some(m) = it.next() {
|
||||||
|
if n % 10 == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
println!("doing something with m: {}", m);
|
||||||
|
}
|
||||||
|
println!("n still is {}", n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn issue1924() {
|
||||||
|
struct S<T>(T);
|
||||||
|
impl<T: Iterator<Item = u32>> S<T> {
|
||||||
|
fn f(&mut self) -> Option<u32> {
|
||||||
|
// Used as a field.
|
||||||
|
while let Some(i) = self.0.next() {
|
||||||
|
if i < 3 || i > 7 {
|
||||||
|
return Some(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f2(&mut self) -> Option<u32> {
|
||||||
|
// Don't lint, self borrowed inside the loop
|
||||||
|
while let Some(i) = self.0.next() {
|
||||||
|
if i == 1 {
|
||||||
|
return self.f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Iterator<Item = u32>> S<(S<T>, Option<u32>)> {
|
||||||
|
fn f3(&mut self) -> Option<u32> {
|
||||||
|
// Don't lint, self borrowed inside the loop
|
||||||
|
while let Some(i) = self.0.0.0.next() {
|
||||||
|
if i == 1 {
|
||||||
|
return self.0.0.f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while let Some(i) = self.0.0.0.next() {
|
||||||
|
if i == 1 {
|
||||||
|
return self.f3();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This one is fine, a different field is borrowed
|
||||||
|
while let Some(i) = self.0.0.0.next() {
|
||||||
|
if i == 1 {
|
||||||
|
return self.0.1.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S2<T>(T, u32);
|
||||||
|
impl<T: Iterator<Item = u32>> Iterator for S2<T> {
|
||||||
|
type Item = u32;
|
||||||
|
fn next(&mut self) -> Option<u32> {
|
||||||
|
self.0.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't lint, field of the iterator is accessed in the loop
|
||||||
|
let mut it = S2(1..40, 0);
|
||||||
|
while let Some(n) = it.next() {
|
||||||
|
if n == it.1 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needs &mut, field of the iterator is accessed after the loop
|
||||||
|
let mut it = S2(1..40, 0);
|
||||||
|
while let Some(n) = it.next() {
|
||||||
|
if n == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("iterator field {}", it.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|
|
@ -19,28 +19,90 @@ LL | while let Some(_) = iter.next() {}
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter`
|
||||||
|
|
||||||
error: this loop could be written as a `for` loop
|
error: this loop could be written as a `for` loop
|
||||||
--> $DIR/while_let_on_iterator.rs:101:9
|
--> $DIR/while_let_on_iterator.rs:94:9
|
||||||
|
|
|
|
||||||
LL | while let Some([..]) = it.next() {}
|
LL | while let Some([..]) = it.next() {}
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it`
|
||||||
|
|
||||||
error: this loop could be written as a `for` loop
|
error: this loop could be written as a `for` loop
|
||||||
--> $DIR/while_let_on_iterator.rs:108:9
|
--> $DIR/while_let_on_iterator.rs:101:9
|
||||||
|
|
|
|
||||||
LL | while let Some([_x]) = it.next() {}
|
LL | while let Some([_x]) = it.next() {}
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it`
|
||||||
|
|
||||||
error: this loop could be written as a `for` loop
|
error: this loop could be written as a `for` loop
|
||||||
--> $DIR/while_let_on_iterator.rs:121:9
|
--> $DIR/while_let_on_iterator.rs:114:9
|
||||||
|
|
|
|
||||||
LL | while let Some(x @ [_]) = it.next() {
|
LL | while let Some(x @ [_]) = it.next() {
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it`
|
||||||
|
|
||||||
error: this loop could be written as a `for` loop
|
error: this loop could be written as a `for` loop
|
||||||
--> $DIR/while_let_on_iterator.rs:153:9
|
--> $DIR/while_let_on_iterator.rs:134:9
|
||||||
|
|
|
|
||||||
LL | while let Some(_) = y.next() {
|
LL | while let Some(_) = y.next() {
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y`
|
||||||
|
|
||||||
error: aborting due to 7 previous errors
|
error: this loop could be written as a `for` loop
|
||||||
|
--> $DIR/while_let_on_iterator.rs:193:9
|
||||||
|
|
|
||||||
|
LL | while let Some(m) = it.next() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it`
|
||||||
|
|
||||||
|
error: this loop could be written as a `for` loop
|
||||||
|
--> $DIR/while_let_on_iterator.rs:204:5
|
||||||
|
|
|
||||||
|
LL | while let Some(n) = it.next() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it`
|
||||||
|
|
||||||
|
error: this loop could be written as a `for` loop
|
||||||
|
--> $DIR/while_let_on_iterator.rs:206:9
|
||||||
|
|
|
||||||
|
LL | while let Some(m) = it.next() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it`
|
||||||
|
|
||||||
|
error: this loop could be written as a `for` loop
|
||||||
|
--> $DIR/while_let_on_iterator.rs:215:9
|
||||||
|
|
|
||||||
|
LL | while let Some(m) = it.next() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it`
|
||||||
|
|
||||||
|
error: this loop could be written as a `for` loop
|
||||||
|
--> $DIR/while_let_on_iterator.rs:224:9
|
||||||
|
|
|
||||||
|
LL | while let Some(m) = it.next() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it`
|
||||||
|
|
||||||
|
error: this loop could be written as a `for` loop
|
||||||
|
--> $DIR/while_let_on_iterator.rs:241:9
|
||||||
|
|
|
||||||
|
LL | while let Some(m) = it.next() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it`
|
||||||
|
|
||||||
|
error: this loop could be written as a `for` loop
|
||||||
|
--> $DIR/while_let_on_iterator.rs:256:13
|
||||||
|
|
|
||||||
|
LL | while let Some(i) = self.0.next() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in &mut self.0`
|
||||||
|
|
||||||
|
error: manual `!RangeInclusive::contains` implementation
|
||||||
|
--> $DIR/while_let_on_iterator.rs:257:20
|
||||||
|
|
|
||||||
|
LL | if i < 3 || i > 7 {
|
||||||
|
| ^^^^^^^^^^^^^^ help: use: `!(3..=7).contains(&i)`
|
||||||
|
|
|
||||||
|
= note: `-D clippy::manual-range-contains` implied by `-D warnings`
|
||||||
|
|
||||||
|
error: this loop could be written as a `for` loop
|
||||||
|
--> $DIR/while_let_on_iterator.rs:288:13
|
||||||
|
|
|
||||||
|
LL | while let Some(i) = self.0.0.0.next() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in &mut self.0.0.0`
|
||||||
|
|
||||||
|
error: this loop could be written as a `for` loop
|
||||||
|
--> $DIR/while_let_on_iterator.rs:315:5
|
||||||
|
|
|
||||||
|
LL | while let Some(n) = it.next() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in &mut it`
|
||||||
|
|
||||||
|
error: aborting due to 17 previous errors
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue