Skip to content

Commit 4603a49

Browse files
committed
mut_range_bound to check for immediate break from loop
1 parent f34c697 commit 4603a49

File tree

1 file changed

+66
-13
lines changed

1 file changed

+66
-13
lines changed

clippy_lints/src/loops/mut_range_bound.rs

Lines changed: 66 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
11
use super::MUT_RANGE_BOUND;
22
use clippy_utils::diagnostics::span_lint;
3-
use clippy_utils::{higher, path_to_local};
3+
use clippy_utils::{get_enclosing_block, higher, path_to_local};
44
use if_chain::if_chain;
5-
use rustc_hir::{BindingAnnotation, Expr, HirId, Node, PatKind};
5+
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
6+
use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind};
67
use rustc_infer::infer::TyCtxtInferExt;
78
use rustc_lint::LateContext;
9+
use rustc_middle::hir::map::Map;
810
use rustc_middle::{mir::FakeReadCause, ty};
911
use rustc_span::source_map::Span;
1012
use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
1113

1214
pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) {
13-
if let Some(higher::Range {
14-
start: Some(start),
15-
end: Some(end),
16-
..
17-
}) = higher::Range::hir(arg)
18-
{
15+
if_chain! {
16+
if let Some(higher::Range {
17+
start: Some(start),
18+
end: Some(end),
19+
..
20+
}) = higher::Range::hir(arg);
1921
let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)];
20-
if mut_ids[0].is_some() || mut_ids[1].is_some() {
22+
if mut_ids[0].is_some() || mut_ids[1].is_some();
23+
then {
2124
let (span_low, span_high) = check_for_mutation(cx, body, &mut_ids);
2225
mut_warn_with_span(cx, span_low);
2326
mut_warn_with_span(cx, span_high);
@@ -70,6 +73,7 @@ fn check_for_mutation<'tcx>(
7073
)
7174
.walk_expr(body);
7275
});
76+
7377
delegate.mutation_span()
7478
}
7579

@@ -87,10 +91,10 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
8791
fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) {
8892
if let ty::BorrowKind::MutBorrow = bk {
8993
if let PlaceBase::Local(id) = cmt.place.base {
90-
if Some(id) == self.hir_id_low {
94+
if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
9195
self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id));
9296
}
93-
if Some(id) == self.hir_id_high {
97+
if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
9498
self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id));
9599
}
96100
}
@@ -99,10 +103,10 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
99103

100104
fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
101105
if let PlaceBase::Local(id) = cmt.place.base {
102-
if Some(id) == self.hir_id_low {
106+
if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
103107
self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id));
104108
}
105-
if Some(id) == self.hir_id_high {
109+
if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
106110
self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id));
107111
}
108112
}
@@ -116,3 +120,52 @@ impl MutatePairDelegate<'_, '_> {
116120
(self.span_low, self.span_high)
117121
}
118122
}
123+
124+
struct BreakAfterExprVisitor {
125+
hir_id: HirId,
126+
past_expr: bool,
127+
past_candidate: bool,
128+
break_after_expr: bool,
129+
}
130+
131+
impl BreakAfterExprVisitor {
132+
pub fn is_found(cx: &LateContext<'_>, hir_id: HirId) -> bool {
133+
let mut visitor = BreakAfterExprVisitor {
134+
hir_id,
135+
past_expr: false,
136+
past_candidate: false,
137+
break_after_expr: false,
138+
};
139+
140+
get_enclosing_block(cx, hir_id).map_or(false, |block| {
141+
visitor.visit_block(block);
142+
visitor.break_after_expr
143+
})
144+
}
145+
}
146+
147+
impl intravisit::Visitor<'tcx> for BreakAfterExprVisitor {
148+
type Map = Map<'tcx>;
149+
150+
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
151+
NestedVisitorMap::None
152+
}
153+
154+
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
155+
if self.past_candidate {
156+
return;
157+
}
158+
159+
if expr.hir_id == self.hir_id {
160+
self.past_expr = true;
161+
} else if self.past_expr {
162+
if matches!(&expr.kind, ExprKind::Break(..)) {
163+
self.break_after_expr = true;
164+
}
165+
166+
self.past_candidate = true;
167+
} else {
168+
intravisit::walk_expr(self, expr);
169+
}
170+
}
171+
}

0 commit comments

Comments
 (0)