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