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