clippy_utils/
visitors.rs

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