]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
Document Arc::from
[rust.git] / src / tools / clippy / clippy_lints / src / loops / mut_range_bound.rs
1 use super::MUT_RANGE_BOUND;
2 use clippy_utils::diagnostics::span_lint;
3 use clippy_utils::{higher, path_to_local};
4 use if_chain::if_chain;
5 use rustc_hir::{BindingAnnotation, Expr, HirId, Node, PatKind};
6 use rustc_infer::infer::TyCtxtInferExt;
7 use rustc_lint::LateContext;
8 use rustc_middle::{mir::FakeReadCause, ty};
9 use rustc_span::source_map::Span;
10 use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
11
12 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     {
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);
24         }
25     }
26 }
27
28 fn mut_warn_with_span(cx: &LateContext<'_>, span: Option<Span>) {
29     if let Some(sp) = span {
30         span_lint(
31             cx,
32             MUT_RANGE_BOUND,
33             sp,
34             "attempt to mutate range bound within loop; note that the range of the loop is unchanged",
35         );
36     }
37 }
38
39 fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId> {
40     if_chain! {
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;
44         then {
45             return Some(hir_id);
46         }
47     }
48     None
49 }
50
51 fn check_for_mutation<'tcx>(
52     cx: &LateContext<'tcx>,
53     body: &Expr<'_>,
54     bound_ids: &[Option<HirId>],
55 ) -> (Option<Span>, Option<Span>) {
56     let mut delegate = MutatePairDelegate {
57         cx,
58         hir_id_low: bound_ids[0],
59         hir_id_high: bound_ids[1],
60         span_low: None,
61         span_high: None,
62     };
63     cx.tcx.infer_ctxt().enter(|infcx| {
64         ExprUseVisitor::new(
65             &mut delegate,
66             &infcx,
67             body.hir_id.owner,
68             cx.param_env,
69             cx.typeck_results(),
70         )
71         .walk_expr(body);
72     });
73     delegate.mutation_span()
74 }
75
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>,
82 }
83
84 impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
85     fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId, _: ConsumeMode) {}
86
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))
92                 }
93                 if Some(id) == self.hir_id_high {
94                     self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id))
95                 }
96             }
97         }
98     }
99
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))
104             }
105             if Some(id) == self.hir_id_high {
106                 self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id))
107             }
108         }
109     }
110
111     fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {}
112 }
113
114 impl MutatePairDelegate<'_, '_> {
115     fn mutation_span(&self) -> (Option<Span>, Option<Span>) {
116         (self.span_low, self.span_high)
117     }
118 }