]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
Auto merge of #83069 - tgnottingham:simplify-query-cache-iter, r=cjgillot
[rust.git] / src / tools / clippy / clippy_lints / src / loops / mut_range_bound.rs
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::ty;
8 use rustc_span::source_map::Span;
9 use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
10
11 pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) {
12     if let Some(higher::Range {
13         start: Some(start),
14         end: Some(end),
15         ..
16     }) = higher::range(arg)
17     {
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);
23         }
24     }
25 }
26
27 fn mut_warn_with_span(cx: &LateContext<'_>, span: Option<Span>) {
28     if let Some(sp) = span {
29         span_lint(
30             cx,
31             MUT_RANGE_BOUND,
32             sp,
33             "attempt to mutate range bound within loop; note that the range of the loop is unchanged",
34         );
35     }
36 }
37
38 fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId> {
39     if_chain! {
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;
43         then {
44             return Some(hir_id);
45         }
46     }
47     None
48 }
49
50 fn check_for_mutation<'tcx>(
51     cx: &LateContext<'tcx>,
52     body: &Expr<'_>,
53     bound_ids: &[Option<HirId>],
54 ) -> (Option<Span>, Option<Span>) {
55     let mut delegate = MutatePairDelegate {
56         cx,
57         hir_id_low: bound_ids[0],
58         hir_id_high: bound_ids[1],
59         span_low: None,
60         span_high: None,
61     };
62     cx.tcx.infer_ctxt().enter(|infcx| {
63         ExprUseVisitor::new(
64             &mut delegate,
65             &infcx,
66             body.hir_id.owner,
67             cx.param_env,
68             cx.typeck_results(),
69         )
70         .walk_expr(body);
71     });
72     delegate.mutation_span()
73 }
74
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>,
81 }
82
83 impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
84     fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId, _: ConsumeMode) {}
85
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))
91                 }
92                 if Some(id) == self.hir_id_high {
93                     self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id))
94                 }
95             }
96         }
97     }
98
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))
103             }
104             if Some(id) == self.hir_id_high {
105                 self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id))
106             }
107         }
108     }
109 }
110
111 impl MutatePairDelegate<'_, '_> {
112     fn mutation_span(&self) -> (Option<Span>, Option<Span>) {
113         (self.span_low, self.span_high)
114     }
115 }