1use crate::get_enclosing_block;
2use crate::msrvs::Msrv;
3use crate::qualify_min_const_fn::is_stable_const_fn;
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, Expr, ExprKind, HirId, ItemId,
12 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 cx: &LateContext<'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: cx.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, 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, 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>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
327 struct V<'a, 'tcx> {
328 cx: &'a LateContext<'tcx>,
329 }
330
331 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
332 type Result = ControlFlow<()>;
333 type NestedFilter = intravisit::nested_filter::None;
334
335 fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result {
336 match e.kind {
337 ExprKind::ConstBlock(_) => return ControlFlow::Continue(()),
338 ExprKind::Call(
339 &Expr {
340 kind: ExprKind::Path(ref p),
341 hir_id,
342 ..
343 },
344 _,
345 ) if self
346 .cx
347 .qpath_res(p, hir_id)
348 .opt_def_id()
349 .is_some_and(|id| is_stable_const_fn(self.cx, id, Msrv::default())) => {},
350 ExprKind::MethodCall(..)
351 if self
352 .cx
353 .typeck_results()
354 .type_dependent_def_id(e.hir_id)
355 .is_some_and(|id| is_stable_const_fn(self.cx, id, Msrv::default())) => {},
356 ExprKind::Binary(_, lhs, rhs)
357 if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty()
358 && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {},
359 ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_raw_ptr() => (),
360 ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (),
361 ExprKind::Index(base, _, _)
362 if matches!(
363 self.cx.typeck_results().expr_ty(base).peel_refs().kind(),
364 ty::Slice(_) | ty::Array(..)
365 ) => {},
366 ExprKind::Path(ref p)
367 if matches!(
368 self.cx.qpath_res(p, e.hir_id),
369 Res::Def(
370 DefKind::Const
371 | DefKind::AssocConst
372 | DefKind::AnonConst
373 | DefKind::ConstParam
374 | DefKind::Ctor(..)
375 | DefKind::Fn
376 | DefKind::AssocFn,
377 _
378 ) | Res::SelfCtor(_)
379 ) => {},
380
381 ExprKind::AddrOf(..)
382 | ExprKind::Array(_)
383 | ExprKind::Block(..)
384 | ExprKind::Cast(..)
385 | ExprKind::DropTemps(_)
386 | ExprKind::Field(..)
387 | ExprKind::If(..)
388 | ExprKind::Let(..)
389 | ExprKind::Lit(_)
390 | ExprKind::Match(..)
391 | ExprKind::Repeat(..)
392 | ExprKind::Struct(..)
393 | ExprKind::Tup(_)
394 | ExprKind::Type(..)
395 | ExprKind::UnsafeBinderCast(..) => (),
396
397 _ => {
398 return ControlFlow::Break(());
399 },
400 }
401
402 walk_expr(self, e)
403 }
404 }
405
406 let mut v = V { cx };
407 v.visit_expr(e).is_continue()
408}
409
410pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
412 struct V<'a, 'tcx> {
413 cx: &'a LateContext<'tcx>,
414 }
415 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
416 type NestedFilter = nested_filter::OnlyBodies;
417 type Result = ControlFlow<()>;
418
419 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
420 self.cx.tcx
421 }
422 fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result {
423 match e.kind {
424 ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_raw_ptr() => {
425 ControlFlow::Break(())
426 },
427 ExprKind::MethodCall(..)
428 if self
429 .cx
430 .typeck_results()
431 .type_dependent_def_id(e.hir_id)
432 .is_some_and(|id| self.cx.tcx.fn_sig(id).skip_binder().safety().is_unsafe()) =>
433 {
434 ControlFlow::Break(())
435 },
436 ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
437 ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().safety().is_unsafe() => {
438 ControlFlow::Break(())
439 },
440 ty::FnPtr(_, hdr) if hdr.safety.is_unsafe() => ControlFlow::Break(()),
441 _ => walk_expr(self, e),
442 },
443 ExprKind::Path(ref p)
444 if self
445 .cx
446 .qpath_res(p, e.hir_id)
447 .opt_def_id()
448 .is_some_and(|id| self.cx.tcx.is_mutable_static(id)) =>
449 {
450 ControlFlow::Break(())
451 },
452 _ => walk_expr(self, e),
453 }
454 }
455 fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result {
456 if matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
457 ControlFlow::Continue(())
458 } else {
459 walk_block(self, b)
460 }
461 }
462 fn visit_nested_item(&mut self, id: ItemId) -> Self::Result {
463 if let ItemKind::Impl(i) = &self.cx.tcx.hir_item(id).kind
464 && let Some(of_trait) = i.of_trait
465 && of_trait.safety.is_unsafe()
466 {
467 ControlFlow::Break(())
468 } else {
469 ControlFlow::Continue(())
470 }
471 }
472 }
473 let mut v = V { cx };
474 v.visit_expr(e).is_break()
475}
476
477pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
479 struct V<'cx, 'tcx> {
480 cx: &'cx LateContext<'tcx>,
481 }
482 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
483 type Result = ControlFlow<()>;
484 type NestedFilter = nested_filter::OnlyBodies;
485 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
486 self.cx.tcx
487 }
488
489 fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result {
490 if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
491 ControlFlow::Break(())
492 } else {
493 walk_block(self, b)
494 }
495 }
496 }
497 let mut v = V { cx };
498 v.visit_expr(e).is_break()
499}
500
501pub fn for_each_value_source<'tcx, B>(
514 e: &'tcx Expr<'tcx>,
515 f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
516) -> ControlFlow<B> {
517 match e.kind {
518 ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f),
519 ExprKind::Match(_, arms, _) => {
520 for arm in arms {
521 for_each_value_source(arm.body, f)?;
522 }
523 ControlFlow::Continue(())
524 },
525 ExprKind::If(_, if_expr, Some(else_expr)) => {
526 for_each_value_source(if_expr, f)?;
527 for_each_value_source(else_expr, f)
528 },
529 ExprKind::DropTemps(e) => for_each_value_source(e, f),
530 _ => f(e),
531 }
532}
533
534pub fn for_each_local_use_after_expr<'tcx, B>(
537 cx: &LateContext<'tcx>,
538 local_id: HirId,
539 expr_id: HirId,
540 f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
541) -> ControlFlow<B> {
542 struct V<'cx, 'tcx, F, B> {
543 cx: &'cx LateContext<'tcx>,
544 local_id: HirId,
545 expr_id: HirId,
546 found: bool,
547 res: ControlFlow<B>,
548 f: F,
549 }
550 impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'_, 'tcx, F, B> {
551 type NestedFilter = nested_filter::OnlyBodies;
552 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
553 self.cx.tcx
554 }
555
556 fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
557 if !self.found {
558 if e.hir_id == self.expr_id {
559 self.found = true;
560 } else {
561 walk_expr(self, e);
562 }
563 return;
564 }
565 if self.res.is_break() {
566 return;
567 }
568 if e.res_local_id() == Some(self.local_id) {
569 self.res = (self.f)(e);
570 } else {
571 walk_expr(self, e);
572 }
573 }
574 }
575
576 if let Some(b) = get_enclosing_block(cx, local_id) {
577 let mut v = V {
578 cx,
579 local_id,
580 expr_id,
581 found: false,
582 res: ControlFlow::Continue(()),
583 f,
584 };
585 v.visit_block(b);
586 v.res
587 } else {
588 ControlFlow::Continue(())
589 }
590}
591
592#[expect(clippy::too_many_lines)]
596pub fn for_each_unconsumed_temporary<'tcx, B>(
597 cx: &LateContext<'tcx>,
598 e: &'tcx Expr<'tcx>,
599 mut f: impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
600) -> ControlFlow<B> {
601 fn helper<'tcx, B>(
603 typeck: &'tcx TypeckResults<'tcx>,
604 consume: bool,
605 e: &'tcx Expr<'tcx>,
606 f: &mut impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
607 ) -> ControlFlow<B> {
608 if !consume
609 || matches!(
610 typeck.expr_adjustments(e),
611 [adjust, ..] if matches!(adjust.kind, Adjust::Borrow(_) | Adjust::Deref(_))
612 )
613 {
614 match e.kind {
615 ExprKind::Path(QPath::Resolved(None, p))
616 if matches!(p.res, Res::Def(DefKind::Ctor(_, CtorKind::Const), _)) =>
617 {
618 f(typeck.expr_ty(e))?;
619 },
620 ExprKind::Path(_)
621 | ExprKind::Unary(UnOp::Deref, _)
622 | ExprKind::Index(..)
623 | ExprKind::Field(..)
624 | ExprKind::AddrOf(..) => (),
625 _ => f(typeck.expr_ty(e))?,
626 }
627 }
628 match e.kind {
629 ExprKind::AddrOf(_, _, e)
630 | ExprKind::Field(e, _)
631 | ExprKind::Unary(UnOp::Deref, e)
632 | ExprKind::Match(e, ..)
633 | ExprKind::Let(&LetExpr { init: e, .. }) => {
634 helper(typeck, false, e, f)?;
635 },
636 ExprKind::Block(&Block { expr: Some(e), .. }, _) | ExprKind::Cast(e, _) | ExprKind::Unary(_, e) => {
637 helper(typeck, true, e, f)?;
638 },
639 ExprKind::Call(callee, args) => {
640 helper(typeck, true, callee, f)?;
641 for arg in args {
642 helper(typeck, true, arg, f)?;
643 }
644 },
645 ExprKind::MethodCall(_, receiver, args, _) => {
646 helper(typeck, true, receiver, f)?;
647 for arg in args {
648 helper(typeck, true, arg, f)?;
649 }
650 },
651 ExprKind::Tup(args) | ExprKind::Array(args) => {
652 for arg in args {
653 helper(typeck, true, arg, f)?;
654 }
655 },
656 ExprKind::Use(expr, _) => {
657 helper(typeck, true, expr, f)?;
658 },
659 ExprKind::Index(borrowed, consumed, _)
660 | ExprKind::Assign(borrowed, consumed, _)
661 | ExprKind::AssignOp(_, borrowed, consumed) => {
662 helper(typeck, false, borrowed, f)?;
663 helper(typeck, true, consumed, f)?;
664 },
665 ExprKind::Binary(_, lhs, rhs) => {
666 helper(typeck, true, lhs, f)?;
667 helper(typeck, true, rhs, f)?;
668 },
669 ExprKind::Struct(_, fields, default) => {
670 for field in fields {
671 helper(typeck, true, field.expr, f)?;
672 }
673 if let StructTailExpr::Base(default) = default {
674 helper(typeck, false, default, f)?;
675 }
676 },
677 ExprKind::If(cond, then, else_expr) => {
678 helper(typeck, true, cond, f)?;
679 helper(typeck, true, then, f)?;
680 if let Some(else_expr) = else_expr {
681 helper(typeck, true, else_expr, f)?;
682 }
683 },
684 ExprKind::Type(e, _) | ExprKind::UnsafeBinderCast(_, e, _) => {
685 helper(typeck, consume, e, f)?;
686 },
687
688 ExprKind::DropTemps(_)
690 | ExprKind::Ret(_)
691 | ExprKind::Become(_)
692 | ExprKind::Break(..)
693 | ExprKind::Yield(..)
694 | ExprKind::Block(..)
695 | ExprKind::Loop(..)
696 | ExprKind::Repeat(..)
697 | ExprKind::Lit(_)
698 | ExprKind::ConstBlock(_)
699 | ExprKind::Closure { .. }
700 | ExprKind::Path(_)
701 | ExprKind::Continue(_)
702 | ExprKind::InlineAsm(_)
703 | ExprKind::OffsetOf(..)
704 | ExprKind::Err(_) => (),
705 }
706 ControlFlow::Continue(())
707 }
708 helper(cx.typeck_results(), true, e, &mut f)
709}
710
711pub fn any_temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
712 for_each_unconsumed_temporary(cx, e, |ty| {
713 if needs_ordered_drop(cx, ty) {
714 ControlFlow::Break(())
715 } else {
716 ControlFlow::Continue(())
717 }
718 })
719 .is_break()
720}
721
722pub fn for_each_local_assignment<'tcx, B>(
725 cx: &LateContext<'tcx>,
726 local_id: HirId,
727 f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
728) -> ControlFlow<B> {
729 struct V<'cx, 'tcx, F, B> {
730 cx: &'cx LateContext<'tcx>,
731 local_id: HirId,
732 res: ControlFlow<B>,
733 f: F,
734 }
735 impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'_, 'tcx, F, B> {
736 type NestedFilter = nested_filter::OnlyBodies;
737 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
738 self.cx.tcx
739 }
740
741 fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
742 if let ExprKind::Assign(lhs, rhs, _) = e.kind
743 && self.res.is_continue()
744 && lhs.res_local_id() == Some(self.local_id)
745 {
746 self.res = (self.f)(rhs);
747 self.visit_expr(rhs);
748 } else {
749 walk_expr(self, e);
750 }
751 }
752 }
753
754 if let Some(b) = get_enclosing_block(cx, local_id) {
755 let mut v = V {
756 cx,
757 local_id,
758 res: ControlFlow::Continue(()),
759 f,
760 };
761 v.visit_block(b);
762 v.res
763 } else {
764 ControlFlow::Continue(())
765 }
766}
767
768pub fn contains_break_or_continue(expr: &Expr<'_>) -> bool {
769 for_each_expr_without_closures(expr, |e| {
770 if matches!(e.kind, ExprKind::Break(..) | ExprKind::Continue(..)) {
771 ControlFlow::Break(())
772 } else {
773 ControlFlow::Continue(())
774 }
775 })
776 .is_some()
777}
778
779pub fn local_used_once<'tcx>(
782 cx: &LateContext<'tcx>,
783 visitable: impl Visitable<'tcx>,
784 id: HirId,
785) -> Option<&'tcx Expr<'tcx>> {
786 let mut expr = None;
787
788 let cf = for_each_expr(cx, visitable, |e| {
789 if e.res_local_id() == Some(id) && expr.replace(e).is_some() {
790 ControlFlow::Break(())
791 } else {
792 ControlFlow::Continue(())
793 }
794 });
795 if cf.is_some() {
796 return None;
797 }
798
799 expr
800}