• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
descend(&self) -> bool21         fn descend(&self) -> bool;
22     }
23 }
24 use internal::Continue;
25 
26 impl Continue for () {
descend(&self) -> bool27     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 {
from(from: bool) -> Self40     fn from(from: bool) -> Self {
41         if from { Self::Yes } else { Self::No }
42     }
43 }
44 impl Continue for Descend {
descend(&self) -> bool45     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.
visit<V: Visitor<'tcx>>(self, visitor: &mut V)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.
for_each_expr<'tcx, B, C: Continue>( node: impl Visitable<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>, ) -> Option<B>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.
for_each_expr_with_closures<'tcx, B, C: Continue>( cx: &LateContext<'tcx>, node: impl Visitable<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>, ) -> Option<B>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
contains_try(expr: &hir::Expr<'_>) -> bool152 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 
find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool where F: FnMut(&'hir hir::Expr<'hir>) -> bool,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.
is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool257 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.
is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool270 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.
is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool282 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.
is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool372 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| {
396                             self.cx.tcx.fn_sig(id).skip_binder().unsafety() == Unsafety::Unsafe
397                         }) =>
398                 {
399                     self.is_unsafe = true;
400                 },
401                 ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
402                     ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().unsafety() == Unsafety::Unsafe => {
403                         self.is_unsafe = true;
404                     },
405                     ty::FnPtr(sig) if sig.unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
406                     _ => walk_expr(self, e),
407                 },
408                 ExprKind::Path(ref p)
409                     if self
410                         .cx
411                         .qpath_res(p, e.hir_id)
412                         .opt_def_id()
413                         .map_or(false, |id| self.cx.tcx.is_mutable_static(id)) =>
414                 {
415                     self.is_unsafe = true;
416                 },
417                 _ => walk_expr(self, e),
418             }
419         }
420         fn visit_block(&mut self, b: &'tcx Block<'_>) {
421             if !matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
422                 walk_block(self, b);
423             }
424         }
425         fn visit_nested_item(&mut self, id: ItemId) {
426             if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind {
427                 self.is_unsafe = i.unsafety == Unsafety::Unsafe;
428             }
429         }
430     }
431     let mut v = V { cx, is_unsafe: false };
432     v.visit_expr(e);
433     v.is_unsafe
434 }
435 
436 /// Checks if the given expression contains an unsafe block
contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool437 pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
438     struct V<'cx, 'tcx> {
439         cx: &'cx LateContext<'tcx>,
440         found_unsafe: bool,
441     }
442     impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
443         type NestedFilter = nested_filter::OnlyBodies;
444         fn nested_visit_map(&mut self) -> Self::Map {
445             self.cx.tcx.hir()
446         }
447 
448         fn visit_block(&mut self, b: &'tcx Block<'_>) {
449             if self.found_unsafe {
450                 return;
451             }
452             if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
453                 self.found_unsafe = true;
454                 return;
455             }
456             walk_block(self, b);
457         }
458     }
459     let mut v = V {
460         cx,
461         found_unsafe: false,
462     };
463     v.visit_expr(e);
464     v.found_unsafe
465 }
466 
467 /// Runs the given function for each sub-expression producing the final value consumed by the parent
468 /// of the give expression.
469 ///
470 /// e.g. for the following expression
471 /// ```rust,ignore
472 /// if foo {
473 ///     f(0)
474 /// } else {
475 ///     1 + 1
476 /// }
477 /// ```
478 /// this will pass both `f(0)` and `1+1` to the given function.
for_each_value_source<'tcx, B>( e: &'tcx Expr<'tcx>, f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, ) -> ControlFlow<B>479 pub fn for_each_value_source<'tcx, B>(
480     e: &'tcx Expr<'tcx>,
481     f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
482 ) -> ControlFlow<B> {
483     match e.kind {
484         ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f),
485         ExprKind::Match(_, arms, _) => {
486             for arm in arms {
487                 for_each_value_source(arm.body, f)?;
488             }
489             ControlFlow::Continue(())
490         },
491         ExprKind::If(_, if_expr, Some(else_expr)) => {
492             for_each_value_source(if_expr, f)?;
493             for_each_value_source(else_expr, f)
494         },
495         ExprKind::DropTemps(e) => for_each_value_source(e, f),
496         _ => f(e),
497     }
498 }
499 
500 /// Runs the given function for each path expression referencing the given local which occur after
501 /// the given expression.
for_each_local_use_after_expr<'tcx, B>( cx: &LateContext<'tcx>, local_id: HirId, expr_id: HirId, f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, ) -> ControlFlow<B>502 pub fn for_each_local_use_after_expr<'tcx, B>(
503     cx: &LateContext<'tcx>,
504     local_id: HirId,
505     expr_id: HirId,
506     f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
507 ) -> ControlFlow<B> {
508     struct V<'cx, 'tcx, F, B> {
509         cx: &'cx LateContext<'tcx>,
510         local_id: HirId,
511         expr_id: HirId,
512         found: bool,
513         res: ControlFlow<B>,
514         f: F,
515     }
516     impl<'cx, 'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'cx, 'tcx, F, B> {
517         type NestedFilter = nested_filter::OnlyBodies;
518         fn nested_visit_map(&mut self) -> Self::Map {
519             self.cx.tcx.hir()
520         }
521 
522         fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
523             if !self.found {
524                 if e.hir_id == self.expr_id {
525                     self.found = true;
526                 } else {
527                     walk_expr(self, e);
528                 }
529                 return;
530             }
531             if self.res.is_break() {
532                 return;
533             }
534             if path_to_local_id(e, self.local_id) {
535                 self.res = (self.f)(e);
536             } else {
537                 walk_expr(self, e);
538             }
539         }
540     }
541 
542     if let Some(b) = get_enclosing_block(cx, local_id) {
543         let mut v = V {
544             cx,
545             local_id,
546             expr_id,
547             found: false,
548             res: ControlFlow::Continue(()),
549             f,
550         };
551         v.visit_block(b);
552         v.res
553     } else {
554         ControlFlow::Continue(())
555     }
556 }
557 
558 // Calls the given function for every unconsumed temporary created by the expression. Note the
559 // function is only guaranteed to be called for types which need to be dropped, but it may be called
560 // for other types.
561 #[allow(clippy::too_many_lines)]
for_each_unconsumed_temporary<'tcx, B>( cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>, mut f: impl FnMut(Ty<'tcx>) -> ControlFlow<B>, ) -> ControlFlow<B>562 pub fn for_each_unconsumed_temporary<'tcx, B>(
563     cx: &LateContext<'tcx>,
564     e: &'tcx Expr<'tcx>,
565     mut f: impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
566 ) -> ControlFlow<B> {
567     // Todo: Handle partially consumed values.
568     fn helper<'tcx, B>(
569         typeck: &'tcx TypeckResults<'tcx>,
570         consume: bool,
571         e: &'tcx Expr<'tcx>,
572         f: &mut impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
573     ) -> ControlFlow<B> {
574         if !consume
575             || matches!(
576                 typeck.expr_adjustments(e),
577                 [adjust, ..] if matches!(adjust.kind, Adjust::Borrow(_) | Adjust::Deref(_))
578             )
579         {
580             match e.kind {
581                 ExprKind::Path(QPath::Resolved(None, p))
582                     if matches!(p.res, Res::Def(DefKind::Ctor(_, CtorKind::Const), _)) =>
583                 {
584                     f(typeck.expr_ty(e))?;
585                 },
586                 ExprKind::Path(_)
587                 | ExprKind::Unary(UnOp::Deref, _)
588                 | ExprKind::Index(..)
589                 | ExprKind::Field(..)
590                 | ExprKind::AddrOf(..) => (),
591                 _ => f(typeck.expr_ty(e))?,
592             }
593         }
594         match e.kind {
595             ExprKind::AddrOf(_, _, e)
596             | ExprKind::Field(e, _)
597             | ExprKind::Unary(UnOp::Deref, e)
598             | ExprKind::Match(e, ..)
599             | ExprKind::Let(&Let { init: e, .. }) => {
600                 helper(typeck, false, e, f)?;
601             },
602             ExprKind::Block(&Block { expr: Some(e), .. }, _) | ExprKind::Cast(e, _) | ExprKind::Unary(_, e) => {
603                 helper(typeck, true, e, f)?;
604             },
605             ExprKind::Call(callee, args) => {
606                 helper(typeck, true, callee, f)?;
607                 for arg in args {
608                     helper(typeck, true, arg, f)?;
609                 }
610             },
611             ExprKind::MethodCall(_, receiver, args, _) => {
612                 helper(typeck, true, receiver, f)?;
613                 for arg in args {
614                     helper(typeck, true, arg, f)?;
615                 }
616             },
617             ExprKind::Tup(args) | ExprKind::Array(args) => {
618                 for arg in args {
619                     helper(typeck, true, arg, f)?;
620                 }
621             },
622             ExprKind::Index(borrowed, consumed)
623             | ExprKind::Assign(borrowed, consumed, _)
624             | ExprKind::AssignOp(_, borrowed, consumed) => {
625                 helper(typeck, false, borrowed, f)?;
626                 helper(typeck, true, consumed, f)?;
627             },
628             ExprKind::Binary(_, lhs, rhs) => {
629                 helper(typeck, true, lhs, f)?;
630                 helper(typeck, true, rhs, f)?;
631             },
632             ExprKind::Struct(_, fields, default) => {
633                 for field in fields {
634                     helper(typeck, true, field.expr, f)?;
635                 }
636                 if let Some(default) = default {
637                     helper(typeck, false, default, f)?;
638                 }
639             },
640             ExprKind::If(cond, then, else_expr) => {
641                 helper(typeck, true, cond, f)?;
642                 helper(typeck, true, then, f)?;
643                 if let Some(else_expr) = else_expr {
644                     helper(typeck, true, else_expr, f)?;
645                 }
646             },
647             ExprKind::Type(e, _) => {
648                 helper(typeck, consume, e, f)?;
649             },
650 
651             // Either drops temporaries, jumps out of the current expression, or has no sub expression.
652             ExprKind::DropTemps(_)
653             | ExprKind::Ret(_)
654             | ExprKind::Become(_)
655             | ExprKind::Break(..)
656             | ExprKind::Yield(..)
657             | ExprKind::Block(..)
658             | ExprKind::Loop(..)
659             | ExprKind::Repeat(..)
660             | ExprKind::Lit(_)
661             | ExprKind::ConstBlock(_)
662             | ExprKind::Closure { .. }
663             | ExprKind::Path(_)
664             | ExprKind::Continue(_)
665             | ExprKind::InlineAsm(_)
666             | ExprKind::OffsetOf(..)
667             | ExprKind::Err(_) => (),
668         }
669         ControlFlow::Continue(())
670     }
671     helper(cx.typeck_results(), true, e, &mut f)
672 }
673 
any_temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool674 pub fn any_temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
675     for_each_unconsumed_temporary(cx, e, |ty| {
676         if needs_ordered_drop(cx, ty) {
677             ControlFlow::Break(())
678         } else {
679             ControlFlow::Continue(())
680         }
681     })
682     .is_break()
683 }
684 
685 /// Runs the given function for each path expression referencing the given local which occur after
686 /// the given expression.
for_each_local_assignment<'tcx, B>( cx: &LateContext<'tcx>, local_id: HirId, f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, ) -> ControlFlow<B>687 pub fn for_each_local_assignment<'tcx, B>(
688     cx: &LateContext<'tcx>,
689     local_id: HirId,
690     f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
691 ) -> ControlFlow<B> {
692     struct V<'cx, 'tcx, F, B> {
693         cx: &'cx LateContext<'tcx>,
694         local_id: HirId,
695         res: ControlFlow<B>,
696         f: F,
697     }
698     impl<'cx, 'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'cx, 'tcx, F, B> {
699         type NestedFilter = nested_filter::OnlyBodies;
700         fn nested_visit_map(&mut self) -> Self::Map {
701             self.cx.tcx.hir()
702         }
703 
704         fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
705             if let ExprKind::Assign(lhs, rhs, _) = e.kind
706                 && self.res.is_continue()
707                 && path_to_local_id(lhs, self.local_id)
708             {
709                 self.res = (self.f)(rhs);
710                 self.visit_expr(rhs);
711             } else {
712                 walk_expr(self, e);
713             }
714         }
715     }
716 
717     if let Some(b) = get_enclosing_block(cx, local_id) {
718         let mut v = V {
719             cx,
720             local_id,
721             res: ControlFlow::Continue(()),
722             f,
723         };
724         v.visit_block(b);
725         v.res
726     } else {
727         ControlFlow::Continue(())
728     }
729 }
730 
contains_break_or_continue(expr: &Expr<'_>) -> bool731 pub fn contains_break_or_continue(expr: &Expr<'_>) -> bool {
732     for_each_expr(expr, |e| {
733         if matches!(e.kind, ExprKind::Break(..) | ExprKind::Continue(..)) {
734             ControlFlow::Break(())
735         } else {
736             ControlFlow::Continue(())
737         }
738     })
739     .is_some()
740 }
741