1 use crate::visitors
::{for_each_expr, for_each_expr_with_closures, Descend, Visitable}
;
2 use crate::{self as utils, get_enclosing_loop_or_multi_call_closure}
;
3 use core
::ops
::ControlFlow
;
5 use rustc_hir
::intravisit
::{self, Visitor}
;
6 use rustc_hir
::{self as hir, Expr, ExprKind, HirId, HirIdSet}
;
7 use rustc_hir_typeck
::expr_use_visitor
::{Delegate, ExprUseVisitor, Place, PlaceBase, PlaceWithHirId}
;
8 use rustc_infer
::infer
::TyCtxtInferExt
;
9 use rustc_lint
::LateContext
;
10 use rustc_middle
::hir
::nested_filter
;
11 use rustc_middle
::mir
::FakeReadCause
;
14 /// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined.
15 pub fn mutated_variables
<'tcx
>(expr
: &'tcx Expr
<'_
>, cx
: &LateContext
<'tcx
>) -> Option
<HirIdSet
> {
16 let mut delegate
= MutVarsDelegate
{
17 used_mutably
: HirIdSet
::default(),
20 let infcx
= cx
.tcx
.infer_ctxt().build();
24 expr
.hir_id
.owner
.def_id
,
33 Some(delegate
.used_mutably
)
36 pub fn is_potentially_mutated
<'tcx
>(variable
: HirId
, expr
: &'tcx Expr
<'_
>, cx
: &LateContext
<'tcx
>) -> bool
{
37 mutated_variables(expr
, cx
).map_or(true, |mutated
| mutated
.contains(&variable
))
40 pub fn is_potentially_local_place(local_id
: HirId
, place
: &Place
<'_
>) -> bool
{
42 PlaceBase
::Local(id
) => id
== local_id
,
43 PlaceBase
::Upvar(_
) => {
44 // Conservatively assume yes.
51 struct MutVarsDelegate
{
52 used_mutably
: HirIdSet
,
56 impl<'tcx
> MutVarsDelegate
{
57 fn update(&mut self, cat
: &PlaceWithHirId
<'tcx
>) {
58 match cat
.place
.base
{
59 PlaceBase
::Local(id
) => {
60 self.used_mutably
.insert(id
);
62 PlaceBase
::Upvar(_
) => {
63 //FIXME: This causes false negatives. We can't get the `NodeId` from
64 //`Categorization::Upvar(_)`. So we search for any `Upvar`s in the
65 //`while`-body, not just the ones in the condition.
73 impl<'tcx
> Delegate
<'tcx
> for MutVarsDelegate
{
74 fn consume(&mut self, _
: &PlaceWithHirId
<'tcx
>, _
: HirId
) {}
76 fn borrow(&mut self, cmt
: &PlaceWithHirId
<'tcx
>, _
: HirId
, bk
: ty
::BorrowKind
) {
77 if bk
== ty
::BorrowKind
::MutBorrow
{
82 fn mutate(&mut self, cmt
: &PlaceWithHirId
<'tcx
>, _
: HirId
) {
86 fn fake_read(&mut self, _
: &rustc_hir_typeck
::expr_use_visitor
::PlaceWithHirId
<'tcx
>, _
: FakeReadCause
, _
: HirId
) {}
89 pub struct ParamBindingIdCollector
{
90 pub binding_hir_ids
: Vec
<hir
::HirId
>,
92 impl<'tcx
> ParamBindingIdCollector
{
93 fn collect_binding_hir_ids(body
: &'tcx hir
::Body
<'tcx
>) -> Vec
<hir
::HirId
> {
94 let mut hir_ids
: Vec
<hir
::HirId
> = Vec
::new();
95 for param
in body
.params
{
96 let mut finder
= ParamBindingIdCollector
{
97 binding_hir_ids
: Vec
::new(),
99 finder
.visit_param(param
);
100 for hir_id
in &finder
.binding_hir_ids
{
101 hir_ids
.push(*hir_id
);
107 impl<'tcx
> intravisit
::Visitor
<'tcx
> for ParamBindingIdCollector
{
108 fn visit_pat(&mut self, pat
: &'tcx hir
::Pat
<'tcx
>) {
109 if let hir
::PatKind
::Binding(_
, hir_id
, ..) = pat
.kind
{
110 self.binding_hir_ids
.push(hir_id
);
112 intravisit
::walk_pat(self, pat
);
116 pub struct BindingUsageFinder
<'a
, 'tcx
> {
117 cx
: &'a LateContext
<'tcx
>,
118 binding_ids
: Vec
<hir
::HirId
>,
121 impl<'a
, 'tcx
> BindingUsageFinder
<'a
, 'tcx
> {
122 pub fn are_params_used(cx
: &'a LateContext
<'tcx
>, body
: &'tcx hir
::Body
<'tcx
>) -> bool
{
123 let mut finder
= BindingUsageFinder
{
125 binding_ids
: ParamBindingIdCollector
::collect_binding_hir_ids(body
),
128 finder
.visit_body(body
);
132 impl<'a
, 'tcx
> intravisit
::Visitor
<'tcx
> for BindingUsageFinder
<'a
, 'tcx
> {
133 type NestedFilter
= nested_filter
::OnlyBodies
;
135 fn visit_expr(&mut self, expr
: &'tcx hir
::Expr
<'tcx
>) {
136 if !self.usage_found
{
137 intravisit
::walk_expr(self, expr
);
141 fn visit_path(&mut self, path
: &hir
::Path
<'tcx
>, _
: hir
::HirId
) {
142 if let Res
::Local(id
) = path
.res
{
143 if self.binding_ids
.contains(&id
) {
144 self.usage_found
= true;
149 fn nested_visit_map(&mut self) -> Self::Map
{
154 pub fn contains_return_break_continue_macro(expression
: &Expr
<'_
>) -> bool
{
155 for_each_expr(expression
, |e
| {
157 ExprKind
::Ret(..) | ExprKind
::Break(..) | ExprKind
::Continue(..) => ControlFlow
::Break(()),
158 // Something special could be done here to handle while or for loop
159 // desugaring, as this will detect a break if there's a while loop
160 // or a for loop inside the expression.
161 _
if e
.span
.from_expansion() => ControlFlow
::Break(()),
162 _
=> ControlFlow
::Continue(()),
168 pub fn local_used_in
<'tcx
>(cx
: &LateContext
<'tcx
>, local_id
: HirId
, v
: impl Visitable
<'tcx
>) -> bool
{
169 for_each_expr_with_closures(cx
, v
, |e
| {
170 if utils
::path_to_local_id(e
, local_id
) {
171 ControlFlow
::Break(())
173 ControlFlow
::Continue(())
179 pub fn local_used_after_expr(cx
: &LateContext
<'_
>, local_id
: HirId
, after
: &Expr
<'_
>) -> bool
{
180 let Some(block
) = utils
::get_enclosing_block(cx
, local_id
) else {
188 // let closure = || local;
191 let loop_start
= get_enclosing_loop_or_multi_call_closure(cx
, after
).map(|e
| e
.hir_id
);
193 let mut past_expr
= false;
194 for_each_expr_with_closures(cx
, block
, |e
| {
196 if utils
::path_to_local_id(e
, local_id
) {
197 ControlFlow
::Break(())
199 ControlFlow
::Continue(Descend
::Yes
)
201 } else if e
.hir_id
== after
.hir_id
{
203 ControlFlow
::Continue(Descend
::No
)
205 past_expr
= Some(e
.hir_id
) == loop_start
;
206 ControlFlow
::Continue(Descend
::Yes
)