1use crate::ty::needs_ordered_drop;
2use crate::{get_enclosing_block, path_to_local_id};
3use core::ops::ControlFlow;
4use rustc_ast::visit::{VisitorResult, try_visit};
5use rustc_hir::def::{CtorKind, DefKind, Res};
6use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr};
7use rustc_hir::{
8 self as hir, AmbigArg, AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId,
9 ItemKind, LetExpr, Pat, QPath, Stmt, StructTailExpr, UnOp, UnsafeSource,
10};
11use rustc_lint::LateContext;
12use rustc_middle::hir::nested_filter;
13use rustc_middle::ty::adjustment::Adjust;
14use rustc_middle::ty::{self, Ty, TyCtxt, TypeckResults};
15use rustc_span::Span;
16
17mod internal {
18 pub trait Continue {
21 fn descend(&self) -> bool;
22 }
23}
24use internal::Continue;
25
26impl Continue for () {
27 fn descend(&self) -> bool {
28 true
29 }
30}
31
32#[derive(Clone, Copy)]
35pub enum Descend {
36 Yes,
37 No,
38}
39impl From<bool> for Descend {
40 fn from(from: bool) -> Self {
41 if from { Self::Yes } else { Self::No }
42 }
43}
44impl Continue for Descend {
45 fn descend(&self) -> bool {
46 matches!(self, Self::Yes)
47 }
48}
49
50pub trait Visitable<'tcx> {
52 fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result;
54}
55impl<'tcx, T> Visitable<'tcx> for &'tcx [T]
56where
57 &'tcx T: Visitable<'tcx>,
58{
59 fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result {
60 for x in self {
61 try_visit!(x.visit(visitor));
62 }
63 V::Result::output()
64 }
65}
66impl<'tcx, A, B> Visitable<'tcx> for (A, B)
67where
68 A: Visitable<'tcx>,
69 B: Visitable<'tcx>,
70{
71 fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result {
72 let (a, b) = self;
73 try_visit!(a.visit(visitor));
74 b.visit(visitor)
75 }
76}
77impl<'tcx, T> Visitable<'tcx> for Option<T>
78where
79 T: Visitable<'tcx>,
80{
81 fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result {
82 if let Some(x) = self {
83 try_visit!(x.visit(visitor));
84 }
85 V::Result::output()
86 }
87}
88macro_rules! visitable_ref {
89 ($t:ident, $f:ident) => {
90 impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> {
91 fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result {
92 visitor.$f(self)
93 }
94 }
95 };
96}
97visitable_ref!(Arm, visit_arm);
98visitable_ref!(Block, visit_block);
99visitable_ref!(Body, visit_body);
100visitable_ref!(Expr, visit_expr);
101visitable_ref!(Stmt, visit_stmt);
102
103pub fn for_each_expr_without_closures<'tcx, B, C: Continue>(
106 node: impl Visitable<'tcx>,
107 f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
108) -> Option<B> {
109 struct V<F> {
110 f: F,
111 }
112 impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<F> {
113 type Result = ControlFlow<B>;
114
115 fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) -> Self::Result {
116 match (self.f)(e) {
117 ControlFlow::Continue(c) if c.descend() => walk_expr(self, e),
118 ControlFlow::Break(b) => ControlFlow::Break(b),
119 ControlFlow::Continue(_) => ControlFlow::Continue(()),
120 }
121 }
122
123 fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result {
125 ControlFlow::Continue(())
126 }
127 fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) -> Self::Result {
128 ControlFlow::Continue(())
129 }
130 fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) -> Self::Result {
131 ControlFlow::Continue(())
132 }
133 fn visit_nested_item(&mut self, _: ItemId) -> Self::Result {
135 ControlFlow::Continue(())
136 }
137 }
138 let mut v = V { f };
139 node.visit(&mut v).break_value()
140}
141
142pub fn for_each_expr<'tcx, B, C: Continue>(
145 cx: &LateContext<'tcx>,
146 node: impl Visitable<'tcx>,
147 f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
148) -> Option<B> {
149 struct V<'tcx, F> {
150 tcx: TyCtxt<'tcx>,
151 f: F,
152 }
153 impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<'tcx, F> {
154 type NestedFilter = nested_filter::OnlyBodies;
155 type Result = ControlFlow<B>;
156
157 fn nested_visit_map(&mut self) -> Self::Map {
158 self.tcx.hir()
159 }
160
161 fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) -> Self::Result {
162 match (self.f)(e) {
163 ControlFlow::Continue(c) if c.descend() => walk_expr(self, e),
164 ControlFlow::Break(b) => ControlFlow::Break(b),
165 ControlFlow::Continue(_) => ControlFlow::Continue(()),
166 }
167 }
168
169 fn visit_anon_const(&mut self, _: &'tcx AnonConst) -> Self::Result {
171 ControlFlow::Continue(())
172 }
173 fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result {
175 ControlFlow::Continue(())
176 }
177 fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) -> Self::Result {
178 ControlFlow::Continue(())
179 }
180 fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) -> Self::Result {
181 ControlFlow::Continue(())
182 }
183 fn visit_nested_item(&mut self, _: ItemId) -> Self::Result {
185 ControlFlow::Continue(())
186 }
187 }
188 let mut v = V { tcx: cx.tcx, f };
189 node.visit(&mut v).break_value()
190}
191
192fn contains_try(expr: &Expr<'_>) -> bool {
194 for_each_expr_without_closures(expr, |e| {
195 if matches!(e.kind, ExprKind::Match(_, _, hir::MatchSource::TryDesugar(_))) {
196 ControlFlow::Break(())
197 } else {
198 ControlFlow::Continue(())
199 }
200 })
201 .is_some()
202}
203
204pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir Expr<'hir>, callback: F) -> bool
205where
206 F: FnMut(&'hir Expr<'hir>) -> bool,
207{
208 struct RetFinder<F> {
209 in_stmt: bool,
210 failed: bool,
211 cb: F,
212 }
213
214 struct WithStmtGuard<'a, F> {
215 val: &'a mut RetFinder<F>,
216 prev_in_stmt: bool,
217 }
218
219 impl<F> RetFinder<F> {
220 fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuard<'_, F> {
221 let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
222 WithStmtGuard {
223 val: self,
224 prev_in_stmt,
225 }
226 }
227 }
228
229 impl<F> std::ops::Deref for WithStmtGuard<'_, F> {
230 type Target = RetFinder<F>;
231
232 fn deref(&self) -> &Self::Target {
233 self.val
234 }
235 }
236
237 impl<F> std::ops::DerefMut for WithStmtGuard<'_, F> {
238 fn deref_mut(&mut self) -> &mut Self::Target {
239 self.val
240 }
241 }
242
243 impl<F> Drop for WithStmtGuard<'_, F> {
244 fn drop(&mut self) {
245 self.val.in_stmt = self.prev_in_stmt;
246 }
247 }
248
249 impl<'hir, F: FnMut(&'hir Expr<'hir>) -> bool> Visitor<'hir> for RetFinder<F> {
250 fn visit_stmt(&mut self, stmt: &'hir Stmt<'_>) {
251 intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt);
252 }
253
254 fn visit_expr(&mut self, expr: &'hir Expr<'_>) {
255 if self.failed {
256 return;
257 }
258 if self.in_stmt {
259 match expr.kind {
260 ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr),
261 _ => walk_expr(self, expr),
262 }
263 } else {
264 match expr.kind {
265 ExprKind::If(cond, then, else_opt) => {
266 self.inside_stmt(true).visit_expr(cond);
267 self.visit_expr(then);
268 if let Some(el) = else_opt {
269 self.visit_expr(el);
270 }
271 },
272 ExprKind::Match(cond, arms, _) => {
273 self.inside_stmt(true).visit_expr(cond);
274 for arm in arms {
275 self.visit_expr(arm.body);
276 }
277 },
278 ExprKind::Block(..) => walk_expr(self, expr),
279 ExprKind::Ret(Some(expr)) => self.visit_expr(expr),
280 _ => self.failed |= !(self.cb)(expr),
281 }
282 }
283 }
284 }
285
286 !contains_try(expr) && {
287 let mut ret_finder = RetFinder {
288 in_stmt: false,
289 failed: false,
290 cb: callback,
291 };
292 ret_finder.visit_expr(expr);
293 !ret_finder.failed
294 }
295}
296
297pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
299 for_each_expr(cx, cx.tcx.hir().body(body).value, |e| {
300 if let ExprKind::Path(p) = &e.kind {
301 if cx.qpath_res(p, e.hir_id) == res {
302 return ControlFlow::Break(());
303 }
304 }
305 ControlFlow::Continue(())
306 })
307 .is_some()
308}
309
310pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
312 for_each_expr(cx, visitable, |e| {
313 if path_to_local_id(e, id) {
314 ControlFlow::Break(())
315 } else {
316 ControlFlow::Continue(())
317 }
318 })
319 .is_some()
320}
321
322pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
324 struct V<'a, 'tcx> {
325 cx: &'a LateContext<'tcx>,
326 }
327
328 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
329 type Result = ControlFlow<()>;
330 type NestedFilter = intravisit::nested_filter::None;
331
332 fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result {
333 match e.kind {
334 ExprKind::ConstBlock(_) => return ControlFlow::Continue(()),
335 ExprKind::Call(
336 &Expr {
337 kind: ExprKind::Path(ref p),
338 hir_id,
339 ..
340 },
341 _,
342 ) if self
343 .cx
344 .qpath_res(p, hir_id)
345 .opt_def_id()
346 .is_some_and(|id| self.cx.tcx.is_const_fn(id)) => {},
347 ExprKind::MethodCall(..)
348 if self
349 .cx
350 .typeck_results()
351 .type_dependent_def_id(e.hir_id)
352 .is_some_and(|id| self.cx.tcx.is_const_fn(id)) => {},
353 ExprKind::Binary(_, lhs, rhs)
354 if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty()
355 && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {},
356 ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_ref() => (),
357 ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (),
358 ExprKind::Index(base, _, _)
359 if matches!(
360 self.cx.typeck_results().expr_ty(base).peel_refs().kind(),
361 ty::Slice(_) | ty::Array(..)
362 ) => {},
363 ExprKind::Path(ref p)
364 if matches!(
365 self.cx.qpath_res(p, e.hir_id),
366 Res::Def(
367 DefKind::Const
368 | DefKind::AssocConst
369 | DefKind::AnonConst
370 | DefKind::ConstParam
371 | DefKind::Ctor(..)
372 | DefKind::Fn
373 | DefKind::AssocFn,
374 _
375 ) | Res::SelfCtor(_)
376 ) => {},
377
378 ExprKind::AddrOf(..)
379 | ExprKind::Array(_)
380 | ExprKind::Block(..)
381 | ExprKind::Cast(..)
382 | ExprKind::DropTemps(_)
383 | ExprKind::Field(..)
384 | ExprKind::If(..)
385 | ExprKind::Let(..)
386 | ExprKind::Lit(_)
387 | ExprKind::Match(..)
388 | ExprKind::Repeat(..)
389 | ExprKind::Struct(..)
390 | ExprKind::Tup(_)
391 | ExprKind::Type(..) => (),
392
393 _ => {
394 return ControlFlow::Break(());
395 },
396 }
397
398 walk_expr(self, e)
399 }
400 }
401
402 let mut v = V { cx };
403 v.visit_expr(e).is_continue()
404}
405
406pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
408 struct V<'a, 'tcx> {
409 cx: &'a LateContext<'tcx>,
410 }
411 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
412 type NestedFilter = nested_filter::OnlyBodies;
413 type Result = ControlFlow<()>;
414
415 fn nested_visit_map(&mut self) -> Self::Map {
416 self.cx.tcx.hir()
417 }
418 fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result {
419 match e.kind {
420 ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_raw_ptr() => {
421 ControlFlow::Break(())
422 },
423 ExprKind::MethodCall(..)
424 if self
425 .cx
426 .typeck_results()
427 .type_dependent_def_id(e.hir_id)
428 .is_some_and(|id| self.cx.tcx.fn_sig(id).skip_binder().safety().is_unsafe()) =>
429 {
430 ControlFlow::Break(())
431 },
432 ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
433 ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().safety().is_unsafe() => {
434 ControlFlow::Break(())
435 },
436 ty::FnPtr(_, hdr) if hdr.safety.is_unsafe() => ControlFlow::Break(()),
437 _ => walk_expr(self, e),
438 },
439 ExprKind::Path(ref p)
440 if self
441 .cx
442 .qpath_res(p, e.hir_id)
443 .opt_def_id()
444 .is_some_and(|id| self.cx.tcx.is_mutable_static(id)) =>
445 {
446 ControlFlow::Break(())
447 },
448 _ => walk_expr(self, e),
449 }
450 }
451 fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result {
452 if matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
453 ControlFlow::Continue(())
454 } else {
455 walk_block(self, b)
456 }
457 }
458 fn visit_nested_item(&mut self, id: ItemId) -> Self::Result {
459 if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind
460 && i.safety.is_unsafe()
461 {
462 ControlFlow::Break(())
463 } else {
464 ControlFlow::Continue(())
465 }
466 }
467 }
468 let mut v = V { cx };
469 v.visit_expr(e).is_break()
470}
471
472pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
474 struct V<'cx, 'tcx> {
475 cx: &'cx LateContext<'tcx>,
476 }
477 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
478 type Result = ControlFlow<()>;
479 type NestedFilter = nested_filter::OnlyBodies;
480 fn nested_visit_map(&mut self) -> Self::Map {
481 self.cx.tcx.hir()
482 }
483
484 fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result {
485 if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
486 ControlFlow::Break(())
487 } else {
488 walk_block(self, b)
489 }
490 }
491 }
492 let mut v = V { cx };
493 v.visit_expr(e).is_break()
494}
495
496pub fn for_each_value_source<'tcx, B>(
509 e: &'tcx Expr<'tcx>,
510 f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
511) -> ControlFlow<B> {
512 match e.kind {
513 ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f),
514 ExprKind::Match(_, arms, _) => {
515 for arm in arms {
516 for_each_value_source(arm.body, f)?;
517 }
518 ControlFlow::Continue(())
519 },
520 ExprKind::If(_, if_expr, Some(else_expr)) => {
521 for_each_value_source(if_expr, f)?;
522 for_each_value_source(else_expr, f)
523 },
524 ExprKind::DropTemps(e) => for_each_value_source(e, f),
525 _ => f(e),
526 }
527}
528
529pub fn for_each_local_use_after_expr<'tcx, B>(
532 cx: &LateContext<'tcx>,
533 local_id: HirId,
534 expr_id: HirId,
535 f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
536) -> ControlFlow<B> {
537 struct V<'cx, 'tcx, F, B> {
538 cx: &'cx LateContext<'tcx>,
539 local_id: HirId,
540 expr_id: HirId,
541 found: bool,
542 res: ControlFlow<B>,
543 f: F,
544 }
545 impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'_, 'tcx, F, B> {
546 type NestedFilter = nested_filter::OnlyBodies;
547 fn nested_visit_map(&mut self) -> Self::Map {
548 self.cx.tcx.hir()
549 }
550
551 fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
552 if !self.found {
553 if e.hir_id == self.expr_id {
554 self.found = true;
555 } else {
556 walk_expr(self, e);
557 }
558 return;
559 }
560 if self.res.is_break() {
561 return;
562 }
563 if path_to_local_id(e, self.local_id) {
564 self.res = (self.f)(e);
565 } else {
566 walk_expr(self, e);
567 }
568 }
569 }
570
571 if let Some(b) = get_enclosing_block(cx, local_id) {
572 let mut v = V {
573 cx,
574 local_id,
575 expr_id,
576 found: false,
577 res: ControlFlow::Continue(()),
578 f,
579 };
580 v.visit_block(b);
581 v.res
582 } else {
583 ControlFlow::Continue(())
584 }
585}
586
587#[allow(clippy::too_many_lines)]
591pub fn for_each_unconsumed_temporary<'tcx, B>(
592 cx: &LateContext<'tcx>,
593 e: &'tcx Expr<'tcx>,
594 mut f: impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
595) -> ControlFlow<B> {
596 fn helper<'tcx, B>(
598 typeck: &'tcx TypeckResults<'tcx>,
599 consume: bool,
600 e: &'tcx Expr<'tcx>,
601 f: &mut impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
602 ) -> ControlFlow<B> {
603 if !consume
604 || matches!(
605 typeck.expr_adjustments(e),
606 [adjust, ..] if matches!(adjust.kind, Adjust::Borrow(_) | Adjust::Deref(_))
607 )
608 {
609 match e.kind {
610 ExprKind::Path(QPath::Resolved(None, p))
611 if matches!(p.res, Res::Def(DefKind::Ctor(_, CtorKind::Const), _)) =>
612 {
613 f(typeck.expr_ty(e))?;
614 },
615 ExprKind::Path(_)
616 | ExprKind::Unary(UnOp::Deref, _)
617 | ExprKind::Index(..)
618 | ExprKind::Field(..)
619 | ExprKind::AddrOf(..) => (),
620 _ => f(typeck.expr_ty(e))?,
621 }
622 }
623 match e.kind {
624 ExprKind::AddrOf(_, _, e)
625 | ExprKind::Field(e, _)
626 | ExprKind::Unary(UnOp::Deref, e)
627 | ExprKind::Match(e, ..)
628 | ExprKind::Let(&LetExpr { init: e, .. }) => {
629 helper(typeck, false, e, f)?;
630 },
631 ExprKind::Block(&Block { expr: Some(e), .. }, _) | ExprKind::Cast(e, _) | ExprKind::Unary(_, e) => {
632 helper(typeck, true, e, f)?;
633 },
634 ExprKind::Call(callee, args) => {
635 helper(typeck, true, callee, f)?;
636 for arg in args {
637 helper(typeck, true, arg, f)?;
638 }
639 },
640 ExprKind::MethodCall(_, receiver, args, _) => {
641 helper(typeck, true, receiver, f)?;
642 for arg in args {
643 helper(typeck, true, arg, f)?;
644 }
645 },
646 ExprKind::Tup(args) | ExprKind::Array(args) => {
647 for arg in args {
648 helper(typeck, true, arg, f)?;
649 }
650 },
651 ExprKind::Index(borrowed, consumed, _)
652 | ExprKind::Assign(borrowed, consumed, _)
653 | ExprKind::AssignOp(_, borrowed, consumed) => {
654 helper(typeck, false, borrowed, f)?;
655 helper(typeck, true, consumed, f)?;
656 },
657 ExprKind::Binary(_, lhs, rhs) => {
658 helper(typeck, true, lhs, f)?;
659 helper(typeck, true, rhs, f)?;
660 },
661 ExprKind::Struct(_, fields, default) => {
662 for field in fields {
663 helper(typeck, true, field.expr, f)?;
664 }
665 if let StructTailExpr::Base(default) = default {
666 helper(typeck, false, default, f)?;
667 }
668 },
669 ExprKind::If(cond, then, else_expr) => {
670 helper(typeck, true, cond, f)?;
671 helper(typeck, true, then, f)?;
672 if let Some(else_expr) = else_expr {
673 helper(typeck, true, else_expr, f)?;
674 }
675 },
676 ExprKind::Type(e, _) => {
677 helper(typeck, consume, e, f)?;
678 },
679 ExprKind::UnsafeBinderCast(_, e, _) => {
680 helper(typeck, consume, e, f)?;
681 },
682
683 ExprKind::DropTemps(_)
685 | ExprKind::Ret(_)
686 | ExprKind::Become(_)
687 | ExprKind::Break(..)
688 | ExprKind::Yield(..)
689 | ExprKind::Block(..)
690 | ExprKind::Loop(..)
691 | ExprKind::Repeat(..)
692 | ExprKind::Lit(_)
693 | ExprKind::ConstBlock(_)
694 | ExprKind::Closure { .. }
695 | ExprKind::Path(_)
696 | ExprKind::Continue(_)
697 | ExprKind::InlineAsm(_)
698 | ExprKind::OffsetOf(..)
699 | ExprKind::Err(_) => (),
700 }
701 ControlFlow::Continue(())
702 }
703 helper(cx.typeck_results(), true, e, &mut f)
704}
705
706pub fn any_temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
707 for_each_unconsumed_temporary(cx, e, |ty| {
708 if needs_ordered_drop(cx, ty) {
709 ControlFlow::Break(())
710 } else {
711 ControlFlow::Continue(())
712 }
713 })
714 .is_break()
715}
716
717pub fn for_each_local_assignment<'tcx, B>(
720 cx: &LateContext<'tcx>,
721 local_id: HirId,
722 f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
723) -> ControlFlow<B> {
724 struct V<'cx, 'tcx, F, B> {
725 cx: &'cx LateContext<'tcx>,
726 local_id: HirId,
727 res: ControlFlow<B>,
728 f: F,
729 }
730 impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'_, 'tcx, F, B> {
731 type NestedFilter = nested_filter::OnlyBodies;
732 fn nested_visit_map(&mut self) -> Self::Map {
733 self.cx.tcx.hir()
734 }
735
736 fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
737 if let ExprKind::Assign(lhs, rhs, _) = e.kind
738 && self.res.is_continue()
739 && path_to_local_id(lhs, self.local_id)
740 {
741 self.res = (self.f)(rhs);
742 self.visit_expr(rhs);
743 } else {
744 walk_expr(self, e);
745 }
746 }
747 }
748
749 if let Some(b) = get_enclosing_block(cx, local_id) {
750 let mut v = V {
751 cx,
752 local_id,
753 res: ControlFlow::Continue(()),
754 f,
755 };
756 v.visit_block(b);
757 v.res
758 } else {
759 ControlFlow::Continue(())
760 }
761}
762
763pub fn contains_break_or_continue(expr: &Expr<'_>) -> bool {
764 for_each_expr_without_closures(expr, |e| {
765 if matches!(e.kind, ExprKind::Break(..) | ExprKind::Continue(..)) {
766 ControlFlow::Break(())
767 } else {
768 ControlFlow::Continue(())
769 }
770 })
771 .is_some()
772}
773
774pub fn local_used_once<'tcx>(
777 cx: &LateContext<'tcx>,
778 visitable: impl Visitable<'tcx>,
779 id: HirId,
780) -> Option<&'tcx Expr<'tcx>> {
781 let mut expr = None;
782
783 let cf = for_each_expr(cx, visitable, |e| {
784 if path_to_local_id(e, id) && expr.replace(e).is_some() {
785 ControlFlow::Break(())
786 } else {
787 ControlFlow::Continue(())
788 }
789 });
790 if cf.is_some() {
791 return None;
792 }
793
794 expr
795}