clippy_utils/
visitors.rs

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