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