clippy_utils/
visitors.rs

1use crate::ty::needs_ordered_drop;
2use crate::{get_enclosing_block, path_to_local_id};
3use core::ops::ControlFlow;
4use rustc_ast::visit::{VisitorResult, try_visit};
5use rustc_hir::def::{CtorKind, DefKind, Res};
6use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr};
7use rustc_hir::{
8    self as hir, AmbigArg, AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId,
9    ItemKind, LetExpr, Pat, QPath, Stmt, StructTailExpr, UnOp, UnsafeSource,
10};
11use rustc_lint::LateContext;
12use rustc_middle::hir::nested_filter;
13use rustc_middle::ty::adjustment::Adjust;
14use rustc_middle::ty::{self, Ty, TyCtxt, TypeckResults};
15use rustc_span::Span;
16
17mod internal {
18    /// Trait for visitor functions to control whether or not to descend to child nodes. Implemented
19    /// for only two types. `()` always descends. `Descend` allows controlled descent.
20    pub trait Continue {
21        fn descend(&self) -> bool;
22    }
23}
24use internal::Continue;
25
26impl Continue for () {
27    fn descend(&self) -> bool {
28        true
29    }
30}
31
32/// Allows for controlled descent when using visitor functions. Use `()` instead when always
33/// descending into child nodes.
34#[derive(Clone, Copy)]
35pub enum Descend {
36    Yes,
37    No,
38}
39impl From<bool> for Descend {
40    fn from(from: bool) -> Self {
41        if from { Self::Yes } else { Self::No }
42    }
43}
44impl Continue for Descend {
45    fn descend(&self) -> bool {
46        matches!(self, Self::Yes)
47    }
48}
49
50/// A type which can be visited.
51pub trait Visitable<'tcx> {
52    /// Calls the corresponding `visit_*` function on the visitor.
53    fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result;
54}
55impl<'tcx, T> Visitable<'tcx> for &'tcx [T]
56where
57    &'tcx T: Visitable<'tcx>,
58{
59    fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result {
60        for x in self {
61            try_visit!(x.visit(visitor));
62        }
63        V::Result::output()
64    }
65}
66impl<'tcx, A, B> Visitable<'tcx> for (A, B)
67where
68    A: Visitable<'tcx>,
69    B: Visitable<'tcx>,
70{
71    fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result {
72        let (a, b) = self;
73        try_visit!(a.visit(visitor));
74        b.visit(visitor)
75    }
76}
77impl<'tcx, T> Visitable<'tcx> for Option<T>
78where
79    T: Visitable<'tcx>,
80{
81    fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result {
82        if let Some(x) = self {
83            try_visit!(x.visit(visitor));
84        }
85        V::Result::output()
86    }
87}
88macro_rules! visitable_ref {
89    ($t:ident, $f:ident) => {
90        impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> {
91            fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result {
92                visitor.$f(self)
93            }
94        }
95    };
96}
97visitable_ref!(Arm, visit_arm);
98visitable_ref!(Block, visit_block);
99visitable_ref!(Body, visit_body);
100visitable_ref!(Expr, visit_expr);
101visitable_ref!(Stmt, visit_stmt);
102
103/// Calls the given function once for each expression contained. This does not enter any bodies or
104/// nested items.
105pub fn for_each_expr_without_closures<'tcx, B, C: Continue>(
106    node: impl Visitable<'tcx>,
107    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
108) -> Option<B> {
109    struct V<F> {
110        f: F,
111    }
112    impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<F> {
113        type Result = ControlFlow<B>;
114
115        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) -> Self::Result {
116            match (self.f)(e) {
117                ControlFlow::Continue(c) if c.descend() => walk_expr(self, e),
118                ControlFlow::Break(b) => ControlFlow::Break(b),
119                ControlFlow::Continue(_) => ControlFlow::Continue(()),
120            }
121        }
122
123        // Avoid unnecessary `walk_*` calls.
124        fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result {
125            ControlFlow::Continue(())
126        }
127        fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) -> Self::Result {
128            ControlFlow::Continue(())
129        }
130        fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) -> Self::Result {
131            ControlFlow::Continue(())
132        }
133        // Avoid monomorphising all `visit_*` functions.
134        fn visit_nested_item(&mut self, _: ItemId) -> Self::Result {
135            ControlFlow::Continue(())
136        }
137    }
138    let mut v = V { f };
139    node.visit(&mut v).break_value()
140}
141
142/// Calls the given function once for each expression contained. This will enter bodies, but not
143/// nested items.
144pub fn for_each_expr<'tcx, B, C: Continue>(
145    cx: &LateContext<'tcx>,
146    node: impl Visitable<'tcx>,
147    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
148) -> Option<B> {
149    struct V<'tcx, F> {
150        tcx: TyCtxt<'tcx>,
151        f: F,
152    }
153    impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<'tcx, F> {
154        type NestedFilter = nested_filter::OnlyBodies;
155        type Result = ControlFlow<B>;
156
157        fn nested_visit_map(&mut self) -> Self::Map {
158            self.tcx.hir()
159        }
160
161        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) -> Self::Result {
162            match (self.f)(e) {
163                ControlFlow::Continue(c) if c.descend() => walk_expr(self, e),
164                ControlFlow::Break(b) => ControlFlow::Break(b),
165                ControlFlow::Continue(_) => ControlFlow::Continue(()),
166            }
167        }
168
169        // Only walk closures
170        fn visit_anon_const(&mut self, _: &'tcx AnonConst) -> Self::Result {
171            ControlFlow::Continue(())
172        }
173        // Avoid unnecessary `walk_*` calls.
174        fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result {
175            ControlFlow::Continue(())
176        }
177        fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) -> Self::Result {
178            ControlFlow::Continue(())
179        }
180        fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) -> Self::Result {
181            ControlFlow::Continue(())
182        }
183        // Avoid monomorphising all `visit_*` functions.
184        fn visit_nested_item(&mut self, _: ItemId) -> Self::Result {
185            ControlFlow::Continue(())
186        }
187    }
188    let mut v = V { tcx: cx.tcx, f };
189    node.visit(&mut v).break_value()
190}
191
192/// returns `true` if expr contains match expr desugared from try
193fn contains_try(expr: &Expr<'_>) -> bool {
194    for_each_expr_without_closures(expr, |e| {
195        if matches!(e.kind, ExprKind::Match(_, _, hir::MatchSource::TryDesugar(_))) {
196            ControlFlow::Break(())
197        } else {
198            ControlFlow::Continue(())
199        }
200    })
201    .is_some()
202}
203
204pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir Expr<'hir>, callback: F) -> bool
205where
206    F: FnMut(&'hir Expr<'hir>) -> bool,
207{
208    struct RetFinder<F> {
209        in_stmt: bool,
210        failed: bool,
211        cb: F,
212    }
213
214    struct WithStmtGuard<'a, F> {
215        val: &'a mut RetFinder<F>,
216        prev_in_stmt: bool,
217    }
218
219    impl<F> RetFinder<F> {
220        fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuard<'_, F> {
221            let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
222            WithStmtGuard {
223                val: self,
224                prev_in_stmt,
225            }
226        }
227    }
228
229    impl<F> std::ops::Deref for WithStmtGuard<'_, F> {
230        type Target = RetFinder<F>;
231
232        fn deref(&self) -> &Self::Target {
233            self.val
234        }
235    }
236
237    impl<F> std::ops::DerefMut for WithStmtGuard<'_, F> {
238        fn deref_mut(&mut self) -> &mut Self::Target {
239            self.val
240        }
241    }
242
243    impl<F> Drop for WithStmtGuard<'_, F> {
244        fn drop(&mut self) {
245            self.val.in_stmt = self.prev_in_stmt;
246        }
247    }
248
249    impl<'hir, F: FnMut(&'hir Expr<'hir>) -> bool> Visitor<'hir> for RetFinder<F> {
250        fn visit_stmt(&mut self, stmt: &'hir Stmt<'_>) {
251            intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt);
252        }
253
254        fn visit_expr(&mut self, expr: &'hir Expr<'_>) {
255            if self.failed {
256                return;
257            }
258            if self.in_stmt {
259                match expr.kind {
260                    ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr),
261                    _ => walk_expr(self, expr),
262                }
263            } else {
264                match expr.kind {
265                    ExprKind::If(cond, then, else_opt) => {
266                        self.inside_stmt(true).visit_expr(cond);
267                        self.visit_expr(then);
268                        if let Some(el) = else_opt {
269                            self.visit_expr(el);
270                        }
271                    },
272                    ExprKind::Match(cond, arms, _) => {
273                        self.inside_stmt(true).visit_expr(cond);
274                        for arm in arms {
275                            self.visit_expr(arm.body);
276                        }
277                    },
278                    ExprKind::Block(..) => walk_expr(self, expr),
279                    ExprKind::Ret(Some(expr)) => self.visit_expr(expr),
280                    _ => self.failed |= !(self.cb)(expr),
281                }
282            }
283        }
284    }
285
286    !contains_try(expr) && {
287        let mut ret_finder = RetFinder {
288            in_stmt: false,
289            failed: false,
290            cb: callback,
291        };
292        ret_finder.visit_expr(expr);
293        !ret_finder.failed
294    }
295}
296
297/// Checks if the given resolved path is used in the given body.
298pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
299    for_each_expr(cx, cx.tcx.hir().body(body).value, |e| {
300        if let ExprKind::Path(p) = &e.kind {
301            if cx.qpath_res(p, e.hir_id) == res {
302                return ControlFlow::Break(());
303            }
304        }
305        ControlFlow::Continue(())
306    })
307    .is_some()
308}
309
310/// Checks if the given local is used.
311pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
312    for_each_expr(cx, visitable, |e| {
313        if path_to_local_id(e, id) {
314            ControlFlow::Break(())
315        } else {
316            ControlFlow::Continue(())
317        }
318    })
319    .is_some()
320}
321
322/// Checks if the given expression is a constant.
323pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
324    struct V<'a, 'tcx> {
325        cx: &'a LateContext<'tcx>,
326    }
327
328    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
329        type Result = ControlFlow<()>;
330        type NestedFilter = intravisit::nested_filter::None;
331
332        fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result {
333            match e.kind {
334                ExprKind::ConstBlock(_) => return ControlFlow::Continue(()),
335                ExprKind::Call(
336                    &Expr {
337                        kind: ExprKind::Path(ref p),
338                        hir_id,
339                        ..
340                    },
341                    _,
342                ) if self
343                    .cx
344                    .qpath_res(p, hir_id)
345                    .opt_def_id()
346                    .is_some_and(|id| self.cx.tcx.is_const_fn(id)) => {},
347                ExprKind::MethodCall(..)
348                    if self
349                        .cx
350                        .typeck_results()
351                        .type_dependent_def_id(e.hir_id)
352                        .is_some_and(|id| self.cx.tcx.is_const_fn(id)) => {},
353                ExprKind::Binary(_, lhs, rhs)
354                    if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty()
355                        && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {},
356                ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_ref() => (),
357                ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (),
358                ExprKind::Index(base, _, _)
359                    if matches!(
360                        self.cx.typeck_results().expr_ty(base).peel_refs().kind(),
361                        ty::Slice(_) | ty::Array(..)
362                    ) => {},
363                ExprKind::Path(ref p)
364                    if matches!(
365                        self.cx.qpath_res(p, e.hir_id),
366                        Res::Def(
367                            DefKind::Const
368                                | DefKind::AssocConst
369                                | DefKind::AnonConst
370                                | DefKind::ConstParam
371                                | DefKind::Ctor(..)
372                                | DefKind::Fn
373                                | DefKind::AssocFn,
374                            _
375                        ) | Res::SelfCtor(_)
376                    ) => {},
377
378                ExprKind::AddrOf(..)
379                | ExprKind::Array(_)
380                | ExprKind::Block(..)
381                | ExprKind::Cast(..)
382                | ExprKind::DropTemps(_)
383                | ExprKind::Field(..)
384                | ExprKind::If(..)
385                | ExprKind::Let(..)
386                | ExprKind::Lit(_)
387                | ExprKind::Match(..)
388                | ExprKind::Repeat(..)
389                | ExprKind::Struct(..)
390                | ExprKind::Tup(_)
391                | ExprKind::Type(..) => (),
392
393                _ => {
394                    return ControlFlow::Break(());
395                },
396            }
397
398            walk_expr(self, e)
399        }
400    }
401
402    let mut v = V { cx };
403    v.visit_expr(e).is_continue()
404}
405
406/// Checks if the given expression performs an unsafe operation outside of an unsafe block.
407pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
408    struct V<'a, 'tcx> {
409        cx: &'a LateContext<'tcx>,
410    }
411    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
412        type NestedFilter = nested_filter::OnlyBodies;
413        type Result = ControlFlow<()>;
414
415        fn nested_visit_map(&mut self) -> Self::Map {
416            self.cx.tcx.hir()
417        }
418        fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result {
419            match e.kind {
420                ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_raw_ptr() => {
421                    ControlFlow::Break(())
422                },
423                ExprKind::MethodCall(..)
424                    if self
425                        .cx
426                        .typeck_results()
427                        .type_dependent_def_id(e.hir_id)
428                        .is_some_and(|id| self.cx.tcx.fn_sig(id).skip_binder().safety().is_unsafe()) =>
429                {
430                    ControlFlow::Break(())
431                },
432                ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
433                    ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().safety().is_unsafe() => {
434                        ControlFlow::Break(())
435                    },
436                    ty::FnPtr(_, hdr) if hdr.safety.is_unsafe() => ControlFlow::Break(()),
437                    _ => walk_expr(self, e),
438                },
439                ExprKind::Path(ref p)
440                    if self
441                        .cx
442                        .qpath_res(p, e.hir_id)
443                        .opt_def_id()
444                        .is_some_and(|id| self.cx.tcx.is_mutable_static(id)) =>
445                {
446                    ControlFlow::Break(())
447                },
448                _ => walk_expr(self, e),
449            }
450        }
451        fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result {
452            if matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
453                ControlFlow::Continue(())
454            } else {
455                walk_block(self, b)
456            }
457        }
458        fn visit_nested_item(&mut self, id: ItemId) -> Self::Result {
459            if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind
460                && i.safety.is_unsafe()
461            {
462                ControlFlow::Break(())
463            } else {
464                ControlFlow::Continue(())
465            }
466        }
467    }
468    let mut v = V { cx };
469    v.visit_expr(e).is_break()
470}
471
472/// Checks if the given expression contains an unsafe block
473pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
474    struct V<'cx, 'tcx> {
475        cx: &'cx LateContext<'tcx>,
476    }
477    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
478        type Result = ControlFlow<()>;
479        type NestedFilter = nested_filter::OnlyBodies;
480        fn nested_visit_map(&mut self) -> Self::Map {
481            self.cx.tcx.hir()
482        }
483
484        fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result {
485            if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
486                ControlFlow::Break(())
487            } else {
488                walk_block(self, b)
489            }
490        }
491    }
492    let mut v = V { cx };
493    v.visit_expr(e).is_break()
494}
495
496/// Runs the given function for each sub-expression producing the final value consumed by the parent
497/// of the give expression.
498///
499/// e.g. for the following expression
500/// ```rust,ignore
501/// if foo {
502///     f(0)
503/// } else {
504///     1 + 1
505/// }
506/// ```
507/// this will pass both `f(0)` and `1+1` to the given function.
508pub fn for_each_value_source<'tcx, B>(
509    e: &'tcx Expr<'tcx>,
510    f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
511) -> ControlFlow<B> {
512    match e.kind {
513        ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f),
514        ExprKind::Match(_, arms, _) => {
515            for arm in arms {
516                for_each_value_source(arm.body, f)?;
517            }
518            ControlFlow::Continue(())
519        },
520        ExprKind::If(_, if_expr, Some(else_expr)) => {
521            for_each_value_source(if_expr, f)?;
522            for_each_value_source(else_expr, f)
523        },
524        ExprKind::DropTemps(e) => for_each_value_source(e, f),
525        _ => f(e),
526    }
527}
528
529/// Runs the given function for each path expression referencing the given local which occur after
530/// the given expression.
531pub fn for_each_local_use_after_expr<'tcx, B>(
532    cx: &LateContext<'tcx>,
533    local_id: HirId,
534    expr_id: HirId,
535    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
536) -> ControlFlow<B> {
537    struct V<'cx, 'tcx, F, B> {
538        cx: &'cx LateContext<'tcx>,
539        local_id: HirId,
540        expr_id: HirId,
541        found: bool,
542        res: ControlFlow<B>,
543        f: F,
544    }
545    impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'_, 'tcx, F, B> {
546        type NestedFilter = nested_filter::OnlyBodies;
547        fn nested_visit_map(&mut self) -> Self::Map {
548            self.cx.tcx.hir()
549        }
550
551        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
552            if !self.found {
553                if e.hir_id == self.expr_id {
554                    self.found = true;
555                } else {
556                    walk_expr(self, e);
557                }
558                return;
559            }
560            if self.res.is_break() {
561                return;
562            }
563            if path_to_local_id(e, self.local_id) {
564                self.res = (self.f)(e);
565            } else {
566                walk_expr(self, e);
567            }
568        }
569    }
570
571    if let Some(b) = get_enclosing_block(cx, local_id) {
572        let mut v = V {
573            cx,
574            local_id,
575            expr_id,
576            found: false,
577            res: ControlFlow::Continue(()),
578            f,
579        };
580        v.visit_block(b);
581        v.res
582    } else {
583        ControlFlow::Continue(())
584    }
585}
586
587// Calls the given function for every unconsumed temporary created by the expression. Note the
588// function is only guaranteed to be called for types which need to be dropped, but it may be called
589// for other types.
590#[allow(clippy::too_many_lines)]
591pub fn for_each_unconsumed_temporary<'tcx, B>(
592    cx: &LateContext<'tcx>,
593    e: &'tcx Expr<'tcx>,
594    mut f: impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
595) -> ControlFlow<B> {
596    // Todo: Handle partially consumed values.
597    fn helper<'tcx, B>(
598        typeck: &'tcx TypeckResults<'tcx>,
599        consume: bool,
600        e: &'tcx Expr<'tcx>,
601        f: &mut impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
602    ) -> ControlFlow<B> {
603        if !consume
604            || matches!(
605                typeck.expr_adjustments(e),
606                [adjust, ..] if matches!(adjust.kind, Adjust::Borrow(_) | Adjust::Deref(_))
607            )
608        {
609            match e.kind {
610                ExprKind::Path(QPath::Resolved(None, p))
611                    if matches!(p.res, Res::Def(DefKind::Ctor(_, CtorKind::Const), _)) =>
612                {
613                    f(typeck.expr_ty(e))?;
614                },
615                ExprKind::Path(_)
616                | ExprKind::Unary(UnOp::Deref, _)
617                | ExprKind::Index(..)
618                | ExprKind::Field(..)
619                | ExprKind::AddrOf(..) => (),
620                _ => f(typeck.expr_ty(e))?,
621            }
622        }
623        match e.kind {
624            ExprKind::AddrOf(_, _, e)
625            | ExprKind::Field(e, _)
626            | ExprKind::Unary(UnOp::Deref, e)
627            | ExprKind::Match(e, ..)
628            | ExprKind::Let(&LetExpr { init: e, .. }) => {
629                helper(typeck, false, e, f)?;
630            },
631            ExprKind::Block(&Block { expr: Some(e), .. }, _) | ExprKind::Cast(e, _) | ExprKind::Unary(_, e) => {
632                helper(typeck, true, e, f)?;
633            },
634            ExprKind::Call(callee, args) => {
635                helper(typeck, true, callee, f)?;
636                for arg in args {
637                    helper(typeck, true, arg, f)?;
638                }
639            },
640            ExprKind::MethodCall(_, receiver, args, _) => {
641                helper(typeck, true, receiver, f)?;
642                for arg in args {
643                    helper(typeck, true, arg, f)?;
644                }
645            },
646            ExprKind::Tup(args) | ExprKind::Array(args) => {
647                for arg in args {
648                    helper(typeck, true, arg, f)?;
649                }
650            },
651            ExprKind::Index(borrowed, consumed, _)
652            | ExprKind::Assign(borrowed, consumed, _)
653            | ExprKind::AssignOp(_, borrowed, consumed) => {
654                helper(typeck, false, borrowed, f)?;
655                helper(typeck, true, consumed, f)?;
656            },
657            ExprKind::Binary(_, lhs, rhs) => {
658                helper(typeck, true, lhs, f)?;
659                helper(typeck, true, rhs, f)?;
660            },
661            ExprKind::Struct(_, fields, default) => {
662                for field in fields {
663                    helper(typeck, true, field.expr, f)?;
664                }
665                if let StructTailExpr::Base(default) = default {
666                    helper(typeck, false, default, f)?;
667                }
668            },
669            ExprKind::If(cond, then, else_expr) => {
670                helper(typeck, true, cond, f)?;
671                helper(typeck, true, then, f)?;
672                if let Some(else_expr) = else_expr {
673                    helper(typeck, true, else_expr, f)?;
674                }
675            },
676            ExprKind::Type(e, _) => {
677                helper(typeck, consume, e, f)?;
678            },
679            ExprKind::UnsafeBinderCast(_, e, _) => {
680                helper(typeck, consume, e, f)?;
681            },
682
683            // Either drops temporaries, jumps out of the current expression, or has no sub expression.
684            ExprKind::DropTemps(_)
685            | ExprKind::Ret(_)
686            | ExprKind::Become(_)
687            | ExprKind::Break(..)
688            | ExprKind::Yield(..)
689            | ExprKind::Block(..)
690            | ExprKind::Loop(..)
691            | ExprKind::Repeat(..)
692            | ExprKind::Lit(_)
693            | ExprKind::ConstBlock(_)
694            | ExprKind::Closure { .. }
695            | ExprKind::Path(_)
696            | ExprKind::Continue(_)
697            | ExprKind::InlineAsm(_)
698            | ExprKind::OffsetOf(..)
699            | ExprKind::Err(_) => (),
700        }
701        ControlFlow::Continue(())
702    }
703    helper(cx.typeck_results(), true, e, &mut f)
704}
705
706pub fn any_temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
707    for_each_unconsumed_temporary(cx, e, |ty| {
708        if needs_ordered_drop(cx, ty) {
709            ControlFlow::Break(())
710        } else {
711            ControlFlow::Continue(())
712        }
713    })
714    .is_break()
715}
716
717/// Runs the given function for each path expression referencing the given local which occur after
718/// the given expression.
719pub fn for_each_local_assignment<'tcx, B>(
720    cx: &LateContext<'tcx>,
721    local_id: HirId,
722    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
723) -> ControlFlow<B> {
724    struct V<'cx, 'tcx, F, B> {
725        cx: &'cx LateContext<'tcx>,
726        local_id: HirId,
727        res: ControlFlow<B>,
728        f: F,
729    }
730    impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'_, 'tcx, F, B> {
731        type NestedFilter = nested_filter::OnlyBodies;
732        fn nested_visit_map(&mut self) -> Self::Map {
733            self.cx.tcx.hir()
734        }
735
736        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
737            if let ExprKind::Assign(lhs, rhs, _) = e.kind
738                && self.res.is_continue()
739                && path_to_local_id(lhs, self.local_id)
740            {
741                self.res = (self.f)(rhs);
742                self.visit_expr(rhs);
743            } else {
744                walk_expr(self, e);
745            }
746        }
747    }
748
749    if let Some(b) = get_enclosing_block(cx, local_id) {
750        let mut v = V {
751            cx,
752            local_id,
753            res: ControlFlow::Continue(()),
754            f,
755        };
756        v.visit_block(b);
757        v.res
758    } else {
759        ControlFlow::Continue(())
760    }
761}
762
763pub fn contains_break_or_continue(expr: &Expr<'_>) -> bool {
764    for_each_expr_without_closures(expr, |e| {
765        if matches!(e.kind, ExprKind::Break(..) | ExprKind::Continue(..)) {
766            ControlFlow::Break(())
767        } else {
768            ControlFlow::Continue(())
769        }
770    })
771    .is_some()
772}
773
774/// If the local is only used once in `visitable` returns the path expression referencing the given
775/// local
776pub fn local_used_once<'tcx>(
777    cx: &LateContext<'tcx>,
778    visitable: impl Visitable<'tcx>,
779    id: HirId,
780) -> Option<&'tcx Expr<'tcx>> {
781    let mut expr = None;
782
783    let cf = for_each_expr(cx, visitable, |e| {
784        if path_to_local_id(e, id) && expr.replace(e).is_some() {
785            ControlFlow::Break(())
786        } else {
787            ControlFlow::Continue(())
788        }
789    });
790    if cf.is_some() {
791        return None;
792    }
793
794    expr
795}