]> git.lizzy.rs Git - rust.git/blob - clippy_utils/src/visitors.rs
rustc_typeck to rustc_hir_analysis
[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 #[allow(clippy::too_many_lines)]
572 pub fn for_each_unconsumed_temporary<'tcx, B>(
573     cx: &LateContext<'tcx>,
574     e: &'tcx Expr<'tcx>,
575     mut f: impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
576 ) -> ControlFlow<B> {
577     // Todo: Handle partially consumed values.
578     fn helper<'tcx, B>(
579         typeck: &'tcx TypeckResults<'tcx>,
580         consume: bool,
581         e: &'tcx Expr<'tcx>,
582         f: &mut impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
583     ) -> ControlFlow<B> {
584         if !consume
585             || matches!(
586                 typeck.expr_adjustments(e),
587                 [adjust, ..] if matches!(adjust.kind, Adjust::Borrow(_) | Adjust::Deref(_))
588             )
589         {
590             match e.kind {
591                 ExprKind::Path(QPath::Resolved(None, p))
592                     if matches!(p.res, Res::Def(DefKind::Ctor(_, CtorKind::Const), _)) =>
593                 {
594                     f(typeck.expr_ty(e))?;
595                 },
596                 ExprKind::Path(_)
597                 | ExprKind::Unary(UnOp::Deref, _)
598                 | ExprKind::Index(..)
599                 | ExprKind::Field(..)
600                 | ExprKind::AddrOf(..) => (),
601                 _ => f(typeck.expr_ty(e))?,
602             }
603         }
604         match e.kind {
605             ExprKind::AddrOf(_, _, e)
606             | ExprKind::Field(e, _)
607             | ExprKind::Unary(UnOp::Deref, e)
608             | ExprKind::Match(e, ..)
609             | ExprKind::Let(&Let { init: e, .. }) => {
610                 helper(typeck, false, e, f)?;
611             },
612             ExprKind::Block(&Block { expr: Some(e), .. }, _)
613             | ExprKind::Box(e)
614             | ExprKind::Cast(e, _)
615             | ExprKind::Unary(_, e) => {
616                 helper(typeck, true, e, f)?;
617             },
618             ExprKind::Call(callee, args) => {
619                 helper(typeck, true, callee, f)?;
620                 for arg in args {
621                     helper(typeck, true, arg, f)?;
622                 }
623             },
624             ExprKind::MethodCall(_, receiver, args, _) => {
625                 helper(typeck, true, receiver, f)?;
626                 for arg in args {
627                     helper(typeck, true, arg, f)?;
628                 }
629             },
630             ExprKind::Tup(args) | ExprKind::Array(args) => {
631                 for arg in args {
632                     helper(typeck, true, arg, f)?;
633                 }
634             },
635             ExprKind::Index(borrowed, consumed)
636             | ExprKind::Assign(borrowed, consumed, _)
637             | ExprKind::AssignOp(_, borrowed, consumed) => {
638                 helper(typeck, false, borrowed, f)?;
639                 helper(typeck, true, consumed, f)?;
640             },
641             ExprKind::Binary(_, lhs, rhs) => {
642                 helper(typeck, true, lhs, f)?;
643                 helper(typeck, true, rhs, f)?;
644             },
645             ExprKind::Struct(_, fields, default) => {
646                 for field in fields {
647                     helper(typeck, true, field.expr, f)?;
648                 }
649                 if let Some(default) = default {
650                     helper(typeck, false, default, f)?;
651                 }
652             },
653             ExprKind::If(cond, then, else_expr) => {
654                 helper(typeck, true, cond, f)?;
655                 helper(typeck, true, then, f)?;
656                 if let Some(else_expr) = else_expr {
657                     helper(typeck, true, else_expr, f)?;
658                 }
659             },
660             ExprKind::Type(e, _) => {
661                 helper(typeck, consume, e, f)?;
662             },
663
664             // Either drops temporaries, jumps out of the current expression, or has no sub expression.
665             ExprKind::DropTemps(_)
666             | ExprKind::Ret(_)
667             | ExprKind::Break(..)
668             | ExprKind::Yield(..)
669             | ExprKind::Block(..)
670             | ExprKind::Loop(..)
671             | ExprKind::Repeat(..)
672             | ExprKind::Lit(_)
673             | ExprKind::ConstBlock(_)
674             | ExprKind::Closure { .. }
675             | ExprKind::Path(_)
676             | ExprKind::Continue(_)
677             | ExprKind::InlineAsm(_)
678             | ExprKind::Err => (),
679         }
680         ControlFlow::Continue(())
681     }
682     helper(cx.typeck_results(), true, e, &mut f)
683 }
684
685 pub fn any_temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
686     for_each_unconsumed_temporary(cx, e, |ty| {
687         if needs_ordered_drop(cx, ty) {
688             ControlFlow::Break(())
689         } else {
690             ControlFlow::Continue(())
691         }
692     })
693     .is_break()
694 }
695
696 /// Runs the given function for each path expression referencing the given local which occur after
697 /// the given expression.
698 pub fn for_each_local_assignment<'tcx, B>(
699     cx: &LateContext<'tcx>,
700     local_id: HirId,
701     f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
702 ) -> ControlFlow<B> {
703     struct V<'cx, 'tcx, F, B> {
704         cx: &'cx LateContext<'tcx>,
705         local_id: HirId,
706         res: ControlFlow<B>,
707         f: F,
708     }
709     impl<'cx, 'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'cx, 'tcx, F, B> {
710         type NestedFilter = nested_filter::OnlyBodies;
711         fn nested_visit_map(&mut self) -> Self::Map {
712             self.cx.tcx.hir()
713         }
714
715         fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
716             if let ExprKind::Assign(lhs, rhs, _) = e.kind
717                 && self.res.is_continue()
718                 && path_to_local_id(lhs, self.local_id)
719             {
720                 self.res = (self.f)(rhs);
721                 self.visit_expr(rhs);
722             } else {
723                 walk_expr(self, e);
724             }
725         }
726     }
727
728     if let Some(b) = get_enclosing_block(cx, local_id) {
729         let mut v = V {
730             cx,
731             local_id,
732             res: ControlFlow::Continue(()),
733             f,
734         };
735         v.visit_block(b);
736         v.res
737     } else {
738         ControlFlow::Continue(())
739     }
740 }