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 && let Some(of_trait) = i.of_trait
464 && of_trait.safety.is_unsafe()
465 {
466 ControlFlow::Break(())
467 } else {
468 ControlFlow::Continue(())
469 }
470 }
471 }
472 let mut v = V { cx };
473 v.visit_expr(e).is_break()
474}
475
476pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
478 struct V<'cx, 'tcx> {
479 cx: &'cx LateContext<'tcx>,
480 }
481 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
482 type Result = ControlFlow<()>;
483 type NestedFilter = nested_filter::OnlyBodies;
484 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
485 self.cx.tcx
486 }
487
488 fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result {
489 if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
490 ControlFlow::Break(())
491 } else {
492 walk_block(self, b)
493 }
494 }
495 }
496 let mut v = V { cx };
497 v.visit_expr(e).is_break()
498}
499
500pub fn for_each_value_source<'tcx, B>(
513 e: &'tcx Expr<'tcx>,
514 f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
515) -> ControlFlow<B> {
516 match e.kind {
517 ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f),
518 ExprKind::Match(_, arms, _) => {
519 for arm in arms {
520 for_each_value_source(arm.body, f)?;
521 }
522 ControlFlow::Continue(())
523 },
524 ExprKind::If(_, if_expr, Some(else_expr)) => {
525 for_each_value_source(if_expr, f)?;
526 for_each_value_source(else_expr, f)
527 },
528 ExprKind::DropTemps(e) => for_each_value_source(e, f),
529 _ => f(e),
530 }
531}
532
533pub fn for_each_local_use_after_expr<'tcx, B>(
536 cx: &LateContext<'tcx>,
537 local_id: HirId,
538 expr_id: HirId,
539 f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
540) -> ControlFlow<B> {
541 struct V<'cx, 'tcx, F, B> {
542 cx: &'cx LateContext<'tcx>,
543 local_id: HirId,
544 expr_id: HirId,
545 found: bool,
546 res: ControlFlow<B>,
547 f: F,
548 }
549 impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'_, 'tcx, F, B> {
550 type NestedFilter = nested_filter::OnlyBodies;
551 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
552 self.cx.tcx
553 }
554
555 fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
556 if !self.found {
557 if e.hir_id == self.expr_id {
558 self.found = true;
559 } else {
560 walk_expr(self, e);
561 }
562 return;
563 }
564 if self.res.is_break() {
565 return;
566 }
567 if path_to_local_id(e, self.local_id) {
568 self.res = (self.f)(e);
569 } else {
570 walk_expr(self, e);
571 }
572 }
573 }
574
575 if let Some(b) = get_enclosing_block(cx, local_id) {
576 let mut v = V {
577 cx,
578 local_id,
579 expr_id,
580 found: false,
581 res: ControlFlow::Continue(()),
582 f,
583 };
584 v.visit_block(b);
585 v.res
586 } else {
587 ControlFlow::Continue(())
588 }
589}
590
591#[allow(clippy::too_many_lines)]
595pub fn for_each_unconsumed_temporary<'tcx, B>(
596 cx: &LateContext<'tcx>,
597 e: &'tcx Expr<'tcx>,
598 mut f: impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
599) -> ControlFlow<B> {
600 fn helper<'tcx, B>(
602 typeck: &'tcx TypeckResults<'tcx>,
603 consume: bool,
604 e: &'tcx Expr<'tcx>,
605 f: &mut impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
606 ) -> ControlFlow<B> {
607 if !consume
608 || matches!(
609 typeck.expr_adjustments(e),
610 [adjust, ..] if matches!(adjust.kind, Adjust::Borrow(_) | Adjust::Deref(_))
611 )
612 {
613 match e.kind {
614 ExprKind::Path(QPath::Resolved(None, p))
615 if matches!(p.res, Res::Def(DefKind::Ctor(_, CtorKind::Const), _)) =>
616 {
617 f(typeck.expr_ty(e))?;
618 },
619 ExprKind::Path(_)
620 | ExprKind::Unary(UnOp::Deref, _)
621 | ExprKind::Index(..)
622 | ExprKind::Field(..)
623 | ExprKind::AddrOf(..) => (),
624 _ => f(typeck.expr_ty(e))?,
625 }
626 }
627 match e.kind {
628 ExprKind::AddrOf(_, _, e)
629 | ExprKind::Field(e, _)
630 | ExprKind::Unary(UnOp::Deref, e)
631 | ExprKind::Match(e, ..)
632 | ExprKind::Let(&LetExpr { init: e, .. }) => {
633 helper(typeck, false, e, f)?;
634 },
635 ExprKind::Block(&Block { expr: Some(e), .. }, _) | ExprKind::Cast(e, _) | ExprKind::Unary(_, e) => {
636 helper(typeck, true, e, f)?;
637 },
638 ExprKind::Call(callee, args) => {
639 helper(typeck, true, callee, f)?;
640 for arg in args {
641 helper(typeck, true, arg, f)?;
642 }
643 },
644 ExprKind::MethodCall(_, receiver, args, _) => {
645 helper(typeck, true, receiver, f)?;
646 for arg in args {
647 helper(typeck, true, arg, f)?;
648 }
649 },
650 ExprKind::Tup(args) | ExprKind::Array(args) => {
651 for arg in args {
652 helper(typeck, true, arg, f)?;
653 }
654 },
655 ExprKind::Use(expr, _) => {
656 helper(typeck, true, expr, f)?;
657 },
658 ExprKind::Index(borrowed, consumed, _)
659 | ExprKind::Assign(borrowed, consumed, _)
660 | ExprKind::AssignOp(_, borrowed, consumed) => {
661 helper(typeck, false, borrowed, f)?;
662 helper(typeck, true, consumed, f)?;
663 },
664 ExprKind::Binary(_, lhs, rhs) => {
665 helper(typeck, true, lhs, f)?;
666 helper(typeck, true, rhs, f)?;
667 },
668 ExprKind::Struct(_, fields, default) => {
669 for field in fields {
670 helper(typeck, true, field.expr, f)?;
671 }
672 if let StructTailExpr::Base(default) = default {
673 helper(typeck, false, default, f)?;
674 }
675 },
676 ExprKind::If(cond, then, else_expr) => {
677 helper(typeck, true, cond, f)?;
678 helper(typeck, true, then, f)?;
679 if let Some(else_expr) = else_expr {
680 helper(typeck, true, else_expr, f)?;
681 }
682 },
683 ExprKind::Type(e, _) | ExprKind::UnsafeBinderCast(_, e, _) => {
684 helper(typeck, consume, e, f)?;
685 },
686
687 ExprKind::DropTemps(_)
689 | ExprKind::Ret(_)
690 | ExprKind::Become(_)
691 | ExprKind::Break(..)
692 | ExprKind::Yield(..)
693 | ExprKind::Block(..)
694 | ExprKind::Loop(..)
695 | ExprKind::Repeat(..)
696 | ExprKind::Lit(_)
697 | ExprKind::ConstBlock(_)
698 | ExprKind::Closure { .. }
699 | ExprKind::Path(_)
700 | ExprKind::Continue(_)
701 | ExprKind::InlineAsm(_)
702 | ExprKind::OffsetOf(..)
703 | ExprKind::Err(_) => (),
704 }
705 ControlFlow::Continue(())
706 }
707 helper(cx.typeck_results(), true, e, &mut f)
708}
709
710pub fn any_temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
711 for_each_unconsumed_temporary(cx, e, |ty| {
712 if needs_ordered_drop(cx, ty) {
713 ControlFlow::Break(())
714 } else {
715 ControlFlow::Continue(())
716 }
717 })
718 .is_break()
719}
720
721pub fn for_each_local_assignment<'tcx, B>(
724 cx: &LateContext<'tcx>,
725 local_id: HirId,
726 f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
727) -> ControlFlow<B> {
728 struct V<'cx, 'tcx, F, B> {
729 cx: &'cx LateContext<'tcx>,
730 local_id: HirId,
731 res: ControlFlow<B>,
732 f: F,
733 }
734 impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'_, 'tcx, F, B> {
735 type NestedFilter = nested_filter::OnlyBodies;
736 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
737 self.cx.tcx
738 }
739
740 fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
741 if let ExprKind::Assign(lhs, rhs, _) = e.kind
742 && self.res.is_continue()
743 && path_to_local_id(lhs, self.local_id)
744 {
745 self.res = (self.f)(rhs);
746 self.visit_expr(rhs);
747 } else {
748 walk_expr(self, e);
749 }
750 }
751 }
752
753 if let Some(b) = get_enclosing_block(cx, local_id) {
754 let mut v = V {
755 cx,
756 local_id,
757 res: ControlFlow::Continue(()),
758 f,
759 };
760 v.visit_block(b);
761 v.res
762 } else {
763 ControlFlow::Continue(())
764 }
765}
766
767pub fn contains_break_or_continue(expr: &Expr<'_>) -> bool {
768 for_each_expr_without_closures(expr, |e| {
769 if matches!(e.kind, ExprKind::Break(..) | ExprKind::Continue(..)) {
770 ControlFlow::Break(())
771 } else {
772 ControlFlow::Continue(())
773 }
774 })
775 .is_some()
776}
777
778pub fn local_used_once<'tcx>(
781 cx: &LateContext<'tcx>,
782 visitable: impl Visitable<'tcx>,
783 id: HirId,
784) -> Option<&'tcx Expr<'tcx>> {
785 let mut expr = None;
786
787 let cf = for_each_expr(cx, visitable, |e| {
788 if path_to_local_id(e, id) && expr.replace(e).is_some() {
789 ControlFlow::Break(())
790 } else {
791 ControlFlow::Continue(())
792 }
793 });
794 if cf.is_some() {
795 return None;
796 }
797
798 expr
799}