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