Skip to content

Commit 6a79a11

Browse files
committed
mut_range_bound to check for immediate break from loop
1 parent de0f3de commit 6a79a11

File tree

1 file changed

+63
-13
lines changed

1 file changed

+63
-13
lines changed

clippy_lints/src/loops/mut_range_bound.rs

Lines changed: 63 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(arg)
18-
{
15+
if_chain! {
16+
if let Some(higher::Range {
17+
start: Some(start),
18+
end: Some(end),
19+
..
20+
}) = higher::range(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,49 @@ impl MutatePairDelegate<'_, '_> {
116120
(self.span_low, self.span_high)
117121
}
118122
}
123+
124+
struct BreakAfterExprVisitor {
125+
hir_id: HirId,
126+
past_expr: bool,
127+
break_after_expr: bool,
128+
}
129+
130+
impl BreakAfterExprVisitor {
131+
pub fn is_found(cx: &LateContext<'_>, hir_id: HirId) -> bool {
132+
let mut visitor = BreakAfterExprVisitor {
133+
hir_id,
134+
past_expr: false,
135+
break_after_expr: false,
136+
};
137+
138+
get_enclosing_block(cx, hir_id).map_or(false, |block| {
139+
visitor.visit_block(block);
140+
visitor.break_after_expr
141+
})
142+
}
143+
}
144+
145+
impl intravisit::Visitor<'tcx> for BreakAfterExprVisitor {
146+
type Map = Map<'tcx>;
147+
148+
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
149+
NestedVisitorMap::None
150+
}
151+
152+
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
153+
if self.break_after_expr {
154+
return;
155+
}
156+
if matches!(&expr.kind, ExprKind::If(..)) || matches!(&expr.kind, ExprKind::Match(..)) {
157+
return;
158+
}
159+
160+
if expr.hir_id == self.hir_id {
161+
self.past_expr = true;
162+
} else if self.past_expr && matches!(&expr.kind, ExprKind::Break(..)) {
163+
self.break_after_expr = true;
164+
} else {
165+
intravisit::walk_expr(self, expr);
166+
}
167+
}
168+
}

0 commit comments

Comments
 (0)