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