Skip to main content

clippy_utils/
visitors.rs

1use crate::get_enclosing_block;
2use crate::msrvs::Msrv;
3use crate::qualify_min_const_fn::is_stable_const_fn_at;
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, walk_qpath};
10use rustc_hir::{
11    self as hir, AmbigArg, AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, CRATE_HIR_ID, ConstBlock, Expr,
12    ExprKind, HirId, ItemId, 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    tcx: TyCtxt<'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, 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.tcx, 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.tcx, 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/// Returns whether the given expression can be evaluated as a constant assuming that all its sub
326/// expressions can be evaluated as constants.
327fn is_const_evaluatable_helper<'tcx>(tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'tcx>, e: &'tcx Expr<'_>) -> bool {
328    match e.kind {
329        ExprKind::Call(
330            &Expr {
331                kind: ExprKind::Path(ref p),
332                hir_id,
333                ..
334            },
335            _,
336        ) if typeck
337            .qpath_res(p, hir_id)
338            .opt_def_id()
339            .is_some_and(|id| is_stable_const_fn_at(tcx, CRATE_HIR_ID, id, Msrv::default())) =>
340        {
341            true
342        },
343        ExprKind::MethodCall(..)
344            if typeck
345                .type_dependent_def_id(e.hir_id)
346                .is_some_and(|id| is_stable_const_fn_at(tcx, CRATE_HIR_ID, id, Msrv::default())) =>
347        {
348            true
349        },
350        ExprKind::Binary(_, lhs, rhs)
351            if typeck.expr_ty(lhs).peel_refs().is_primitive_ty()
352                && typeck.expr_ty(rhs).peel_refs().is_primitive_ty() =>
353        {
354            true
355        },
356        ExprKind::Unary(UnOp::Deref, e) if typeck.expr_ty(e).is_raw_ptr() => true,
357        ExprKind::Unary(_, e) if typeck.expr_ty(e).peel_refs().is_primitive_ty() => true,
358        ExprKind::Index(base, _, _)
359            if matches!(typeck.expr_ty(base).peel_refs().kind(), ty::Slice(_) | ty::Array(..)) =>
360        {
361            true
362        },
363        ExprKind::Path(ref p)
364            if matches!(
365                typeck.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            true
379        },
380
381        ExprKind::AddrOf(..)
382        | ExprKind::Array(_)
383        | ExprKind::Block(..)
384        | ExprKind::Cast(..)
385        | ExprKind::ConstBlock(_)
386        | ExprKind::DropTemps(_)
387        | ExprKind::Field(..)
388        | ExprKind::If(..)
389        | ExprKind::Let(..)
390        | ExprKind::Lit(_)
391        | ExprKind::Match(..)
392        | ExprKind::Repeat(..)
393        | ExprKind::Struct(..)
394        | ExprKind::Tup(_)
395        | ExprKind::Type(..)
396        | ExprKind::UnsafeBinderCast(..) => true,
397
398        _ => false,
399    }
400}
401
402/// Checks if the given expression can be evaluated as a constant at the specified node
403pub fn is_const_evaluatable<'tcx>(tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'tcx>, e: &'tcx Expr<'_>) -> bool {
404    for_each_expr(tcx, e, move |e| {
405        if !is_const_evaluatable_helper(tcx, typeck, e) {
406            ControlFlow::Break(())
407        } else if matches!(e.kind, ExprKind::ConstBlock(_)) {
408            ControlFlow::Continue(Descend::No)
409        } else {
410            ControlFlow::Continue(Descend::Yes)
411        }
412    })
413    .is_none()
414}
415
416/// Checks if the given expression can be used as a const parameter.
417///
418/// This is more strict than `is_const_evaluatable` as it requires that the expression does not
419/// contain any type parameters or any operations on const parameters.
420pub fn is_const_param_evaluatable<'tcx>(
421    tcx: TyCtxt<'tcx>,
422    typeck: &'tcx TypeckResults<'tcx>,
423    e: &'tcx Expr<'_>,
424) -> bool {
425    struct V1<'tcx> {
426        tcx: TyCtxt<'tcx>,
427        typeck: &'tcx TypeckResults<'tcx>,
428    }
429    impl<'tcx> Visitor<'tcx> for V1<'tcx> {
430        type NestedFilter = nested_filter::OnlyBodies;
431        type Result = ControlFlow<()>;
432        fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
433            self.tcx
434        }
435        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) -> Self::Result {
436            if !is_const_evaluatable_helper(self.tcx, self.typeck, e) {
437                return ControlFlow::Break(());
438            }
439            walk_expr(self, e)
440        }
441        fn visit_inline_const(&mut self, c: &'tcx ConstBlock) -> Self::Result {
442            V2(self.tcx).visit_inline_const(c)
443        }
444        fn visit_anon_const(&mut self, c: &'tcx AnonConst) -> Self::Result {
445            V2(self.tcx).visit_anon_const(c)
446        }
447        fn visit_qpath(&mut self, qpath: &'tcx QPath<'tcx>, id: HirId, _span: Span) -> Self::Result {
448            if matches!(qpath.basic_res(), Res::Def(DefKind::ConstParam | DefKind::TyParam, _)) {
449                return ControlFlow::Break(());
450            }
451            walk_qpath(self, qpath, id)
452        }
453    }
454
455    struct V2<'tcx>(TyCtxt<'tcx>);
456    impl<'tcx> Visitor<'tcx> for V2<'tcx> {
457        type NestedFilter = nested_filter::OnlyBodies;
458        type Result = ControlFlow<()>;
459        fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
460            self.0
461        }
462        fn visit_qpath(&mut self, qpath: &'tcx QPath<'tcx>, id: HirId, _span: Span) -> Self::Result {
463            if matches!(qpath.basic_res(), Res::Def(DefKind::ConstParam | DefKind::TyParam, _)) {
464                return ControlFlow::Break(());
465            }
466            walk_qpath(self, qpath, id)
467        }
468    }
469
470    // We have to work around limitations of `#![feature(generic_const_exprs)]` being unstable.
471    // A const parameter (e.g. `N` in `fn foo<const N: usize>()`) can be used as is, but any
472    // expression involving a const parameter (e.g. `N * 2`) will be a compiler error.
473    // Any type parameter (e.g. `T` in `fn foo<T>()`) will be a compiler error.
474
475    if let ExprKind::Path(ref qpath) = e.kind
476        && matches!(qpath.basic_res(), Res::Def(DefKind::ConstParam, _))
477    {
478        return true;
479    }
480
481    V1 { tcx, typeck }.visit_expr(e).is_continue()
482}
483
484/// Checks if the given expression performs an unsafe operation outside of an unsafe block.
485pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
486    struct V<'a, 'tcx> {
487        cx: &'a LateContext<'tcx>,
488    }
489    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
490        type NestedFilter = nested_filter::OnlyBodies;
491        type Result = ControlFlow<()>;
492
493        fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
494            self.cx.tcx
495        }
496        fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result {
497            match e.kind {
498                ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_raw_ptr() => {
499                    ControlFlow::Break(())
500                },
501                ExprKind::MethodCall(..)
502                    if self
503                        .cx
504                        .typeck_results()
505                        .type_dependent_def_id(e.hir_id)
506                        .is_some_and(|id| self.cx.tcx.fn_sig(id).skip_binder().safety().is_unsafe()) =>
507                {
508                    ControlFlow::Break(())
509                },
510                ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
511                    ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().safety().is_unsafe() => {
512                        ControlFlow::Break(())
513                    },
514                    ty::FnPtr(_, hdr) if hdr.safety().is_unsafe() => ControlFlow::Break(()),
515                    _ => walk_expr(self, e),
516                },
517                ExprKind::Path(ref p)
518                    if self
519                        .cx
520                        .qpath_res(p, e.hir_id)
521                        .opt_def_id()
522                        .is_some_and(|id| self.cx.tcx.is_mutable_static(id)) =>
523                {
524                    ControlFlow::Break(())
525                },
526                _ => walk_expr(self, e),
527            }
528        }
529        fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result {
530            if matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
531                ControlFlow::Continue(())
532            } else {
533                walk_block(self, b)
534            }
535        }
536        fn visit_nested_item(&mut self, id: ItemId) -> Self::Result {
537            if let ItemKind::Impl(i) = &self.cx.tcx.hir_item(id).kind
538                && let Some(of_trait) = i.of_trait
539                && of_trait.safety.is_unsafe()
540            {
541                ControlFlow::Break(())
542            } else {
543                ControlFlow::Continue(())
544            }
545        }
546    }
547    let mut v = V { cx };
548    v.visit_expr(e).is_break()
549}
550
551/// Checks if the given expression contains an unsafe block
552pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
553    struct V<'cx, 'tcx> {
554        cx: &'cx LateContext<'tcx>,
555    }
556    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
557        type Result = ControlFlow<()>;
558        type NestedFilter = nested_filter::OnlyBodies;
559        fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
560            self.cx.tcx
561        }
562
563        fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result {
564            if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
565                ControlFlow::Break(())
566            } else {
567                walk_block(self, b)
568            }
569        }
570    }
571    let mut v = V { cx };
572    v.visit_expr(e).is_break()
573}
574
575/// Runs the given function for each sub-expression producing the final value consumed by the parent
576/// of the give expression.
577///
578/// e.g. for the following expression
579/// ```rust,ignore
580/// if foo {
581///     f(0)
582/// } else {
583///     1 + 1
584/// }
585/// ```
586/// this will pass both `f(0)` and `1+1` to the given function.
587pub fn for_each_value_source<'tcx, B>(
588    e: &'tcx Expr<'tcx>,
589    f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
590) -> ControlFlow<B> {
591    match e.kind {
592        ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f),
593        ExprKind::Match(_, arms, _) => {
594            for arm in arms {
595                for_each_value_source(arm.body, f)?;
596            }
597            ControlFlow::Continue(())
598        },
599        ExprKind::If(_, if_expr, Some(else_expr)) => {
600            for_each_value_source(if_expr, f)?;
601            for_each_value_source(else_expr, f)
602        },
603        ExprKind::DropTemps(e) => for_each_value_source(e, f),
604        _ => f(e),
605    }
606}
607
608/// Runs the given function for each path expression referencing the given local which occur after
609/// the given expression.
610pub fn for_each_local_use_after_expr<'tcx, B>(
611    cx: &LateContext<'tcx>,
612    local_id: HirId,
613    expr_id: HirId,
614    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
615) -> ControlFlow<B> {
616    struct V<'cx, 'tcx, F, B> {
617        cx: &'cx LateContext<'tcx>,
618        local_id: HirId,
619        expr_id: HirId,
620        found: bool,
621        res: ControlFlow<B>,
622        f: F,
623    }
624    impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'_, 'tcx, F, B> {
625        type NestedFilter = nested_filter::OnlyBodies;
626        fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
627            self.cx.tcx
628        }
629
630        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
631            if !self.found {
632                if e.hir_id == self.expr_id {
633                    self.found = true;
634                } else {
635                    walk_expr(self, e);
636                }
637                return;
638            }
639            if self.res.is_break() {
640                return;
641            }
642            if e.res_local_id() == Some(self.local_id) {
643                self.res = (self.f)(e);
644            } else {
645                walk_expr(self, e);
646            }
647        }
648    }
649
650    if let Some(b) = get_enclosing_block(cx, local_id) {
651        let mut v = V {
652            cx,
653            local_id,
654            expr_id,
655            found: false,
656            res: ControlFlow::Continue(()),
657            f,
658        };
659        v.visit_block(b);
660        v.res
661    } else {
662        ControlFlow::Continue(())
663    }
664}
665
666// Calls the given function for every unconsumed temporary created by the expression. Note the
667// function is only guaranteed to be called for types which need to be dropped, but it may be called
668// for other types.
669#[expect(clippy::too_many_lines)]
670pub fn for_each_unconsumed_temporary<'tcx, B>(
671    cx: &LateContext<'tcx>,
672    e: &'tcx Expr<'tcx>,
673    mut f: impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
674) -> ControlFlow<B> {
675    // Todo: Handle partially consumed values.
676    fn helper<'tcx, B>(
677        typeck: &'tcx TypeckResults<'tcx>,
678        consume: bool,
679        e: &'tcx Expr<'tcx>,
680        f: &mut impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
681    ) -> ControlFlow<B> {
682        if !consume
683            || matches!(
684                typeck.expr_adjustments(e),
685                [adjust, ..] if matches!(adjust.kind, Adjust::Borrow(_) | Adjust::Deref(_))
686            )
687        {
688            match e.kind {
689                ExprKind::Path(QPath::Resolved(None, p))
690                    if matches!(p.res, Res::Def(DefKind::Ctor(_, CtorKind::Const), _)) =>
691                {
692                    f(typeck.expr_ty(e))?;
693                },
694                ExprKind::Path(_)
695                | ExprKind::Unary(UnOp::Deref, _)
696                | ExprKind::Index(..)
697                | ExprKind::Field(..)
698                | ExprKind::AddrOf(..) => (),
699                _ => f(typeck.expr_ty(e))?,
700            }
701        }
702        match e.kind {
703            ExprKind::AddrOf(_, _, e)
704            | ExprKind::Field(e, _)
705            | ExprKind::Unary(UnOp::Deref, e)
706            | ExprKind::Match(e, ..)
707            | ExprKind::Let(&LetExpr { init: e, .. }) => {
708                helper(typeck, false, e, f)?;
709            },
710            ExprKind::Block(&Block { expr: Some(e), .. }, _) | ExprKind::Cast(e, _) | ExprKind::Unary(_, e) => {
711                helper(typeck, true, e, f)?;
712            },
713            ExprKind::Call(callee, args) => {
714                helper(typeck, true, callee, f)?;
715                for arg in args {
716                    helper(typeck, true, arg, f)?;
717                }
718            },
719            ExprKind::MethodCall(_, receiver, args, _) => {
720                helper(typeck, true, receiver, f)?;
721                for arg in args {
722                    helper(typeck, true, arg, f)?;
723                }
724            },
725            ExprKind::Tup(args) | ExprKind::Array(args) => {
726                for arg in args {
727                    helper(typeck, true, arg, f)?;
728                }
729            },
730            ExprKind::Use(expr, _) => {
731                helper(typeck, true, expr, f)?;
732            },
733            ExprKind::Index(borrowed, consumed, _)
734            | ExprKind::Assign(borrowed, consumed, _)
735            | ExprKind::AssignOp(_, borrowed, consumed) => {
736                helper(typeck, false, borrowed, f)?;
737                helper(typeck, true, consumed, f)?;
738            },
739            ExprKind::Binary(_, lhs, rhs) => {
740                helper(typeck, true, lhs, f)?;
741                helper(typeck, true, rhs, f)?;
742            },
743            ExprKind::Struct(_, fields, default) => {
744                for field in fields {
745                    helper(typeck, true, field.expr, f)?;
746                }
747                if let StructTailExpr::Base(default) = default {
748                    helper(typeck, false, default, f)?;
749                }
750            },
751            ExprKind::If(cond, then, else_expr) => {
752                helper(typeck, true, cond, f)?;
753                helper(typeck, true, then, f)?;
754                if let Some(else_expr) = else_expr {
755                    helper(typeck, true, else_expr, f)?;
756                }
757            },
758            ExprKind::Type(e, _) | ExprKind::UnsafeBinderCast(_, e, _) => {
759                helper(typeck, consume, e, f)?;
760            },
761
762            // Either drops temporaries, jumps out of the current expression, or has no sub expression.
763            ExprKind::DropTemps(_)
764            | ExprKind::Ret(_)
765            | ExprKind::Become(_)
766            | ExprKind::Break(..)
767            | ExprKind::Yield(..)
768            | ExprKind::Block(..)
769            | ExprKind::Loop(..)
770            | ExprKind::Repeat(..)
771            | ExprKind::Lit(_)
772            | ExprKind::ConstBlock(_)
773            | ExprKind::Closure { .. }
774            | ExprKind::Path(_)
775            | ExprKind::Continue(_)
776            | ExprKind::InlineAsm(_)
777            | ExprKind::OffsetOf(..)
778            | ExprKind::Err(_) => (),
779        }
780        ControlFlow::Continue(())
781    }
782    helper(cx.typeck_results(), true, e, &mut f)
783}
784
785pub fn any_temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
786    for_each_unconsumed_temporary(cx, e, |ty| {
787        if needs_ordered_drop(cx, ty) {
788            ControlFlow::Break(())
789        } else {
790            ControlFlow::Continue(())
791        }
792    })
793    .is_break()
794}
795
796/// Runs the given function for each path expression referencing the given local which occur after
797/// the given expression.
798pub fn for_each_local_assignment<'tcx, B>(
799    cx: &LateContext<'tcx>,
800    local_id: HirId,
801    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
802) -> ControlFlow<B> {
803    struct V<'cx, 'tcx, F, B> {
804        cx: &'cx LateContext<'tcx>,
805        local_id: HirId,
806        res: ControlFlow<B>,
807        f: F,
808    }
809    impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'_, 'tcx, F, B> {
810        type NestedFilter = nested_filter::OnlyBodies;
811        fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
812            self.cx.tcx
813        }
814
815        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
816            if let ExprKind::Assign(lhs, rhs, _) = e.kind
817                && self.res.is_continue()
818                && lhs.res_local_id() == Some(self.local_id)
819            {
820                self.res = (self.f)(rhs);
821                self.visit_expr(rhs);
822            } else {
823                walk_expr(self, e);
824            }
825        }
826    }
827
828    if let Some(b) = get_enclosing_block(cx, local_id) {
829        let mut v = V {
830            cx,
831            local_id,
832            res: ControlFlow::Continue(()),
833            f,
834        };
835        v.visit_block(b);
836        v.res
837    } else {
838        ControlFlow::Continue(())
839    }
840}
841
842pub fn contains_break_or_continue(expr: &Expr<'_>) -> bool {
843    for_each_expr_without_closures(expr, |e| {
844        if matches!(e.kind, ExprKind::Break(..) | ExprKind::Continue(..)) {
845            ControlFlow::Break(())
846        } else {
847            ControlFlow::Continue(())
848        }
849    })
850    .is_some()
851}
852
853/// If the local is only used once in `visitable` returns the path expression referencing the given
854/// local
855pub fn local_used_once<'tcx>(
856    cx: &LateContext<'tcx>,
857    visitable: impl Visitable<'tcx>,
858    id: HirId,
859) -> Option<&'tcx Expr<'tcx>> {
860    let mut expr = None;
861
862    let cf = for_each_expr(cx.tcx, visitable, |e| {
863        if e.res_local_id() == Some(id) && expr.replace(e).is_some() {
864            ControlFlow::Break(())
865        } else {
866            ControlFlow::Continue(())
867        }
868    });
869    if cf.is_some() {
870        return None;
871    }
872
873    expr
874}