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