1 use super::MUT_RANGE_BOUND;
2 use crate::utils::{higher, path_to_local, span_lint};
3 use if_chain::if_chain;
4 use rustc_hir::{BindingAnnotation, Expr, HirId, Node, PatKind};
5 use rustc_infer::infer::TyCtxtInferExt;
6 use rustc_lint::LateContext;
7 use rustc_middle::mir::FakeReadCause;
9 use rustc_span::source_map::Span;
10 use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
12 pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) {
13 if let Some(higher::Range {
17 }) = higher::range(arg)
19 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() {
21 let (span_low, span_high) = check_for_mutation(cx, body, &mut_ids);
22 mut_warn_with_span(cx, span_low);
23 mut_warn_with_span(cx, span_high);
28 fn mut_warn_with_span(cx: &LateContext<'_>, span: Option<Span>) {
29 if let Some(sp) = span {
34 "attempt to mutate range bound within loop; note that the range of the loop is unchanged",
39 fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId> {
41 if let Some(hir_id) = path_to_local(bound);
42 if let Node::Binding(pat) = cx.tcx.hir().get(hir_id);
43 if let PatKind::Binding(BindingAnnotation::Mutable, ..) = pat.kind;
51 fn check_for_mutation<'tcx>(
52 cx: &LateContext<'tcx>,
54 bound_ids: &[Option<HirId>],
55 ) -> (Option<Span>, Option<Span>) {
56 let mut delegate = MutatePairDelegate {
58 hir_id_low: bound_ids[0],
59 hir_id_high: bound_ids[1],
63 cx.tcx.infer_ctxt().enter(|infcx| {
73 delegate.mutation_span()
76 struct MutatePairDelegate<'a, 'tcx> {
77 cx: &'a LateContext<'tcx>,
78 hir_id_low: Option<HirId>,
79 hir_id_high: Option<HirId>,
80 span_low: Option<Span>,
81 span_high: Option<Span>,
84 impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
85 fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId, _: ConsumeMode) {}
87 fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) {
88 if let ty::BorrowKind::MutBorrow = bk {
89 if let PlaceBase::Local(id) = cmt.place.base {
90 if Some(id) == self.hir_id_low {
91 self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id))
93 if Some(id) == self.hir_id_high {
94 self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id))
100 fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
101 if let PlaceBase::Local(id) = cmt.place.base {
102 if Some(id) == self.hir_id_low {
103 self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id))
105 if Some(id) == self.hir_id_high {
106 self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id))
111 fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _:HirId) { }
114 impl MutatePairDelegate<'_, '_> {
115 fn mutation_span(&self) -> (Option<Span>, Option<Span>) {
116 (self.span_low, self.span_high)