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;
8 use rustc_span::source_map::Span;
9 use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
11 pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) {
12 if let Some(higher::Range {
16 }) = higher::range(arg)
18 let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)];
19 if mut_ids[0].is_some() || mut_ids[1].is_some() {
20 let (span_low, span_high) = check_for_mutation(cx, body, &mut_ids);
21 mut_warn_with_span(cx, span_low);
22 mut_warn_with_span(cx, span_high);
27 fn mut_warn_with_span(cx: &LateContext<'_>, span: Option<Span>) {
28 if let Some(sp) = span {
33 "attempt to mutate range bound within loop; note that the range of the loop is unchanged",
38 fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId> {
40 if let Some(hir_id) = path_to_local(bound);
41 if let Node::Binding(pat) = cx.tcx.hir().get(hir_id);
42 if let PatKind::Binding(BindingAnnotation::Mutable, ..) = pat.kind;
50 fn check_for_mutation<'tcx>(
51 cx: &LateContext<'tcx>,
53 bound_ids: &[Option<HirId>],
54 ) -> (Option<Span>, Option<Span>) {
55 let mut delegate = MutatePairDelegate {
57 hir_id_low: bound_ids[0],
58 hir_id_high: bound_ids[1],
62 cx.tcx.infer_ctxt().enter(|infcx| {
72 delegate.mutation_span()
75 struct MutatePairDelegate<'a, 'tcx> {
76 cx: &'a LateContext<'tcx>,
77 hir_id_low: Option<HirId>,
78 hir_id_high: Option<HirId>,
79 span_low: Option<Span>,
80 span_high: Option<Span>,
83 impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
84 fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId, _: ConsumeMode) {}
86 fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) {
87 if let ty::BorrowKind::MutBorrow = bk {
88 if let PlaceBase::Local(id) = cmt.place.base {
89 if Some(id) == self.hir_id_low {
90 self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id))
92 if Some(id) == self.hir_id_high {
93 self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id))
99 fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
100 if let PlaceBase::Local(id) = cmt.place.base {
101 if Some(id) == self.hir_id_low {
102 self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id))
104 if Some(id) == self.hir_id_high {
105 self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id))
111 impl MutatePairDelegate<'_, '_> {
112 fn mutation_span(&self) -> (Option<Span>, Option<Span>) {
113 (self.span_low, self.span_high)