1use crate::get_enclosing_block;
2use crate::msrvs::Msrv;
3use crate::qualify_min_const_fn::is_stable_const_fn_at;
4use crate::res::MaybeResPath as _;
5use crate::ty::needs_ordered_drop;
6use core::ops::ControlFlow;
7use rustc_ast::visit::{VisitorResult as _, try_visit};
8use rustc_hir::def::{CtorKind, DefKind, Res};
9use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr, walk_qpath};
10use rustc_hir::{
11 self as hir, AmbigArg, AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, CRATE_HIR_ID, ConstBlock, Expr,
12 ExprKind, HirId, 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
211where
212 F: FnMut(&'hir Expr<'hir>) -> bool,
213{
214 struct RetFinder<F> {
215 in_stmt: bool,
216 failed: bool,
217 cb: F,
218 }
219
220 struct WithStmtGuard<'a, F> {
221 val: &'a mut RetFinder<F>,
222 prev_in_stmt: bool,
223 }
224
225 impl<F> RetFinder<F> {
226 fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuard<'_, F> {
227 let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
228 WithStmtGuard {
229 val: self,
230 prev_in_stmt,
231 }
232 }
233 }
234
235 impl<F> std::ops::Deref for WithStmtGuard<'_, F> {
236 type Target = RetFinder<F>;
237
238 fn deref(&self) -> &Self::Target {
239 self.val
240 }
241 }
242
243 impl<F> std::ops::DerefMut for WithStmtGuard<'_, F> {
244 fn deref_mut(&mut self) -> &mut Self::Target {
245 self.val
246 }
247 }
248
249 impl<F> Drop for WithStmtGuard<'_, F> {
250 fn drop(&mut self) {
251 self.val.in_stmt = self.prev_in_stmt;
252 }
253 }
254
255 impl<'hir, F: FnMut(&'hir Expr<'hir>) -> bool> Visitor<'hir> for RetFinder<F> {
256 fn visit_stmt(&mut self, stmt: &'hir Stmt<'_>) {
257 intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt);
258 }
259
260 fn visit_expr(&mut self, expr: &'hir Expr<'_>) {
261 if self.failed {
262 return;
263 }
264 if self.in_stmt {
265 match expr.kind {
266 ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr),
267 _ => walk_expr(self, expr),
268 }
269 } else {
270 match expr.kind {
271 ExprKind::If(cond, then, else_opt) => {
272 self.inside_stmt(true).visit_expr(cond);
273 self.visit_expr(then);
274 if let Some(el) = else_opt {
275 self.visit_expr(el);
276 }
277 },
278 ExprKind::Match(cond, arms, _) => {
279 self.inside_stmt(true).visit_expr(cond);
280 for arm in arms {
281 self.visit_expr(arm.body);
282 }
283 },
284 ExprKind::Block(..) => walk_expr(self, expr),
285 ExprKind::Ret(Some(expr)) => self.visit_expr(expr),
286 _ => self.failed |= !(self.cb)(expr),
287 }
288 }
289 }
290 }
291
292 !contains_try(expr) && {
293 let mut ret_finder = RetFinder {
294 in_stmt: false,
295 failed: false,
296 cb: callback,
297 };
298 ret_finder.visit_expr(expr);
299 !ret_finder.failed
300 }
301}
302
303pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
305 for_each_expr(cx.tcx, cx.tcx.hir_body(body).value, |e| {
306 if let ExprKind::Path(p) = &e.kind
307 && cx.qpath_res(p, e.hir_id) == res
308 {
309 return ControlFlow::Break(());
310 }
311 ControlFlow::Continue(())
312 })
313 .is_some()
314}
315
316pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
318 for_each_expr(cx.tcx, visitable, |e| {
319 if e.res_local_id() == Some(id) {
320 ControlFlow::Break(())
321 } else {
322 ControlFlow::Continue(())
323 }
324 })
325 .is_some()
326}
327
328fn is_const_evaluatable_helper<'tcx>(tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'tcx>, e: &'tcx Expr<'_>) -> bool {
331 match e.kind {
332 ExprKind::Call(
333 &Expr {
334 kind: ExprKind::Path(ref p),
335 hir_id,
336 ..
337 },
338 _,
339 ) if typeck
340 .qpath_res(p, hir_id)
341 .opt_def_id()
342 .is_some_and(|id| is_stable_const_fn_at(tcx, CRATE_HIR_ID, id, Msrv::default())) =>
343 {
344 true
345 },
346 ExprKind::MethodCall(..)
347 if typeck
348 .type_dependent_def_id(e.hir_id)
349 .is_some_and(|id| is_stable_const_fn_at(tcx, CRATE_HIR_ID, id, Msrv::default())) =>
350 {
351 true
352 },
353 ExprKind::Binary(_, lhs, rhs)
354 if typeck.expr_ty(lhs).peel_refs().is_primitive_ty()
355 && typeck.expr_ty(rhs).peel_refs().is_primitive_ty() =>
356 {
357 true
358 },
359 ExprKind::Unary(UnOp::Deref, e) if typeck.expr_ty(e).is_raw_ptr() => true,
360 ExprKind::Unary(_, e) if typeck.expr_ty(e).peel_refs().is_primitive_ty() => true,
361 ExprKind::Index(base, _, _)
362 if matches!(typeck.expr_ty(base).peel_refs().kind(), ty::Slice(_) | ty::Array(..)) =>
363 {
364 true
365 },
366 ExprKind::Path(ref p)
367 if matches!(
368 typeck.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 true
382 },
383
384 ExprKind::AddrOf(..)
385 | ExprKind::Array(_)
386 | ExprKind::Block(..)
387 | ExprKind::Cast(..)
388 | ExprKind::ConstBlock(_)
389 | ExprKind::DropTemps(_)
390 | ExprKind::Field(..)
391 | ExprKind::If(..)
392 | ExprKind::Let(..)
393 | ExprKind::Lit(_)
394 | ExprKind::Match(..)
395 | ExprKind::Repeat(..)
396 | ExprKind::Struct(..)
397 | ExprKind::Tup(_)
398 | ExprKind::Type(..)
399 | ExprKind::UnsafeBinderCast(..) => true,
400
401 _ => false,
402 }
403}
404
405pub fn is_const_evaluatable<'tcx>(tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'tcx>, e: &'tcx Expr<'_>) -> bool {
407 for_each_expr(tcx, e, move |e| {
408 if !is_const_evaluatable_helper(tcx, typeck, e) {
409 ControlFlow::Break(())
410 } else if matches!(e.kind, ExprKind::ConstBlock(_)) {
411 ControlFlow::Continue(Descend::No)
412 } else {
413 ControlFlow::Continue(Descend::Yes)
414 }
415 })
416 .is_none()
417}
418
419pub fn is_const_param_evaluatable<'tcx>(
424 tcx: TyCtxt<'tcx>,
425 typeck: &'tcx TypeckResults<'tcx>,
426 e: &'tcx Expr<'_>,
427) -> bool {
428 struct V1<'tcx> {
429 tcx: TyCtxt<'tcx>,
430 typeck: &'tcx TypeckResults<'tcx>,
431 }
432 impl<'tcx> Visitor<'tcx> for V1<'tcx> {
433 type NestedFilter = nested_filter::OnlyBodies;
434 type Result = ControlFlow<()>;
435 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
436 self.tcx
437 }
438 fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) -> Self::Result {
439 if !is_const_evaluatable_helper(self.tcx, self.typeck, e) {
440 return ControlFlow::Break(());
441 }
442 walk_expr(self, e)
443 }
444 fn visit_inline_const(&mut self, c: &'tcx ConstBlock) -> Self::Result {
445 V2(self.tcx).visit_inline_const(c)
446 }
447 fn visit_anon_const(&mut self, c: &'tcx AnonConst) -> Self::Result {
448 V2(self.tcx).visit_anon_const(c)
449 }
450 fn visit_qpath(&mut self, qpath: &'tcx QPath<'tcx>, id: HirId, _span: Span) -> Self::Result {
451 if matches!(qpath.basic_res(), Res::Def(DefKind::ConstParam | DefKind::TyParam, _)) {
452 return ControlFlow::Break(());
453 }
454 walk_qpath(self, qpath, id)
455 }
456 }
457
458 struct V2<'tcx>(TyCtxt<'tcx>);
459 impl<'tcx> Visitor<'tcx> for V2<'tcx> {
460 type NestedFilter = nested_filter::OnlyBodies;
461 type Result = ControlFlow<()>;
462 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
463 self.0
464 }
465 fn visit_qpath(&mut self, qpath: &'tcx QPath<'tcx>, id: HirId, _span: Span) -> Self::Result {
466 if matches!(qpath.basic_res(), Res::Def(DefKind::ConstParam | DefKind::TyParam, _)) {
467 return ControlFlow::Break(());
468 }
469 walk_qpath(self, qpath, id)
470 }
471 }
472
473 if let ExprKind::Path(ref qpath) = e.kind
479 && matches!(qpath.basic_res(), Res::Def(DefKind::ConstParam, _))
480 {
481 return true;
482 }
483
484 V1 { tcx, typeck }.visit_expr(e).is_continue()
485}
486
487pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
489 struct V<'a, 'tcx> {
490 cx: &'a LateContext<'tcx>,
491 }
492 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
493 type NestedFilter = nested_filter::OnlyBodies;
494 type Result = ControlFlow<()>;
495
496 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
497 self.cx.tcx
498 }
499 fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result {
500 match e.kind {
501 ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_raw_ptr() => {
502 ControlFlow::Break(())
503 },
504 ExprKind::MethodCall(..)
505 if self
506 .cx
507 .typeck_results()
508 .type_dependent_def_id(e.hir_id)
509 .is_some_and(|id| self.cx.tcx.fn_sig(id).skip_binder().safety().is_unsafe()) =>
510 {
511 ControlFlow::Break(())
512 },
513 ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
514 ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().safety().is_unsafe() => {
515 ControlFlow::Break(())
516 },
517 ty::FnPtr(_, hdr) if hdr.safety().is_unsafe() => ControlFlow::Break(()),
518 _ => walk_expr(self, e),
519 },
520 ExprKind::Path(ref p)
521 if self
522 .cx
523 .qpath_res(p, e.hir_id)
524 .opt_def_id()
525 .is_some_and(|id| self.cx.tcx.is_mutable_static(id)) =>
526 {
527 ControlFlow::Break(())
528 },
529 _ => walk_expr(self, e),
530 }
531 }
532 fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result {
533 if matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
534 ControlFlow::Continue(())
535 } else {
536 walk_block(self, b)
537 }
538 }
539 fn visit_nested_item(&mut self, id: ItemId) -> Self::Result {
540 if let ItemKind::Impl(i) = &self.cx.tcx.hir_item(id).kind
541 && let Some(of_trait) = i.of_trait
542 && of_trait.safety.is_unsafe()
543 {
544 ControlFlow::Break(())
545 } else {
546 ControlFlow::Continue(())
547 }
548 }
549 }
550 let mut v = V { cx };
551 v.visit_expr(e).is_break()
552}
553
554pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
556 struct V<'cx, 'tcx> {
557 cx: &'cx LateContext<'tcx>,
558 }
559 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
560 type Result = ControlFlow<()>;
561 type NestedFilter = nested_filter::OnlyBodies;
562 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
563 self.cx.tcx
564 }
565
566 fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result {
567 if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
568 ControlFlow::Break(())
569 } else {
570 walk_block(self, b)
571 }
572 }
573 }
574 let mut v = V { cx };
575 v.visit_expr(e).is_break()
576}
577
578pub fn for_each_value_source<'tcx, B>(
591 e: &'tcx Expr<'tcx>,
592 f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
593) -> ControlFlow<B> {
594 match e.kind {
595 ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f),
596 ExprKind::Match(_, arms, _) => {
597 for arm in arms {
598 for_each_value_source(arm.body, f)?;
599 }
600 ControlFlow::Continue(())
601 },
602 ExprKind::If(_, if_expr, Some(else_expr)) => {
603 for_each_value_source(if_expr, f)?;
604 for_each_value_source(else_expr, f)
605 },
606 ExprKind::DropTemps(e) => for_each_value_source(e, f),
607 _ => f(e),
608 }
609}
610
611pub fn for_each_local_use_after_expr<'tcx, B>(
614 cx: &LateContext<'tcx>,
615 local_id: HirId,
616 expr_id: HirId,
617 f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
618) -> ControlFlow<B> {
619 struct V<'cx, 'tcx, F, B> {
620 cx: &'cx LateContext<'tcx>,
621 local_id: HirId,
622 expr_id: HirId,
623 found: bool,
624 res: ControlFlow<B>,
625 f: F,
626 }
627 impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'_, 'tcx, F, B> {
628 type NestedFilter = nested_filter::OnlyBodies;
629 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
630 self.cx.tcx
631 }
632
633 fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
634 if !self.found {
635 if e.hir_id == self.expr_id {
636 self.found = true;
637 } else {
638 walk_expr(self, e);
639 }
640 return;
641 }
642 if self.res.is_break() {
643 return;
644 }
645 if e.res_local_id() == Some(self.local_id) {
646 self.res = (self.f)(e);
647 } else {
648 walk_expr(self, e);
649 }
650 }
651 }
652
653 if let Some(b) = get_enclosing_block(cx, local_id) {
654 let mut v = V {
655 cx,
656 local_id,
657 expr_id,
658 found: false,
659 res: ControlFlow::Continue(()),
660 f,
661 };
662 v.visit_block(b);
663 v.res
664 } else {
665 ControlFlow::Continue(())
666 }
667}
668
669#[expect(clippy::too_many_lines)]
673pub fn for_each_unconsumed_temporary<'tcx, B>(
674 cx: &LateContext<'tcx>,
675 e: &'tcx Expr<'tcx>,
676 mut f: impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
677) -> ControlFlow<B> {
678 fn helper<'tcx, B>(
680 typeck: &'tcx TypeckResults<'tcx>,
681 consume: bool,
682 e: &'tcx Expr<'tcx>,
683 f: &mut impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
684 ) -> ControlFlow<B> {
685 if !consume
686 || matches!(
687 typeck.expr_adjustments(e),
688 [adjust, ..] if matches!(adjust.kind, Adjust::Borrow(_) | Adjust::Deref(_))
689 )
690 {
691 match e.kind {
692 ExprKind::Path(QPath::Resolved(None, p))
693 if matches!(p.res, Res::Def(DefKind::Ctor(_, CtorKind::Const), _)) =>
694 {
695 f(typeck.expr_ty(e))?;
696 },
697 ExprKind::Path(_)
698 | ExprKind::Unary(UnOp::Deref, _)
699 | ExprKind::Index(..)
700 | ExprKind::Field(..)
701 | ExprKind::AddrOf(..) => (),
702 _ => f(typeck.expr_ty(e))?,
703 }
704 }
705 match e.kind {
706 ExprKind::AddrOf(_, _, e)
707 | ExprKind::Field(e, _)
708 | ExprKind::Unary(UnOp::Deref, e)
709 | ExprKind::Match(e, ..)
710 | ExprKind::Let(&LetExpr { init: e, .. }) => {
711 helper(typeck, false, e, f)?;
712 },
713 ExprKind::Block(&Block { expr: Some(e), .. }, _) | ExprKind::Cast(e, _) | ExprKind::Unary(_, e) => {
714 helper(typeck, true, e, f)?;
715 },
716 ExprKind::Call(callee, args) => {
717 helper(typeck, true, callee, f)?;
718 for arg in args {
719 helper(typeck, true, arg, f)?;
720 }
721 },
722 ExprKind::MethodCall(_, receiver, args, _) => {
723 helper(typeck, true, receiver, f)?;
724 for arg in args {
725 helper(typeck, true, arg, f)?;
726 }
727 },
728 ExprKind::Tup(args) | ExprKind::Array(args) => {
729 for arg in args {
730 helper(typeck, true, arg, f)?;
731 }
732 },
733 ExprKind::Use(expr, _) => {
734 helper(typeck, true, expr, f)?;
735 },
736 ExprKind::Index(borrowed, consumed, _)
737 | ExprKind::Assign(borrowed, consumed, _)
738 | ExprKind::AssignOp(_, borrowed, consumed) => {
739 helper(typeck, false, borrowed, f)?;
740 helper(typeck, true, consumed, f)?;
741 },
742 ExprKind::Binary(_, lhs, rhs) => {
743 helper(typeck, true, lhs, f)?;
744 helper(typeck, true, rhs, f)?;
745 },
746 ExprKind::Struct(_, fields, default) => {
747 for field in fields {
748 helper(typeck, true, field.expr, f)?;
749 }
750 if let StructTailExpr::Base(default) = default {
751 helper(typeck, false, default, f)?;
752 }
753 },
754 ExprKind::If(cond, then, else_expr) => {
755 helper(typeck, true, cond, f)?;
756 helper(typeck, true, then, f)?;
757 if let Some(else_expr) = else_expr {
758 helper(typeck, true, else_expr, f)?;
759 }
760 },
761 ExprKind::Type(e, _) | ExprKind::UnsafeBinderCast(_, e, _) => {
762 helper(typeck, consume, e, f)?;
763 },
764
765 ExprKind::DropTemps(_)
767 | ExprKind::Ret(_)
768 | ExprKind::Become(_)
769 | ExprKind::Break(..)
770 | ExprKind::Yield(..)
771 | ExprKind::Block(..)
772 | ExprKind::Loop(..)
773 | ExprKind::Repeat(..)
774 | ExprKind::Lit(_)
775 | ExprKind::ConstBlock(_)
776 | ExprKind::Closure { .. }
777 | ExprKind::Path(_)
778 | ExprKind::Continue(_)
779 | ExprKind::InlineAsm(_)
780 | ExprKind::OffsetOf(..)
781 | ExprKind::Err(_) => (),
782 }
783 ControlFlow::Continue(())
784 }
785 helper(cx.typeck_results(), true, e, &mut f)
786}
787
788pub fn any_temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
791 for_each_unconsumed_temporary(cx, e, |ty| {
792 if needs_ordered_drop(cx, ty) {
793 ControlFlow::Break(())
794 } else {
795 ControlFlow::Continue(())
796 }
797 })
798 .is_break()
799}
800
801pub fn for_each_local_assignment<'tcx, B>(
804 cx: &LateContext<'tcx>,
805 local_id: HirId,
806 f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
807) -> ControlFlow<B> {
808 struct V<'cx, 'tcx, F, B> {
809 cx: &'cx LateContext<'tcx>,
810 local_id: HirId,
811 res: ControlFlow<B>,
812 f: F,
813 }
814 impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'_, 'tcx, F, B> {
815 type NestedFilter = nested_filter::OnlyBodies;
816 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
817 self.cx.tcx
818 }
819
820 fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
821 if let ExprKind::Assign(lhs, rhs, _) = e.kind
822 && self.res.is_continue()
823 && lhs.res_local_id() == Some(self.local_id)
824 {
825 self.res = (self.f)(rhs);
826 self.visit_expr(rhs);
827 } else {
828 walk_expr(self, e);
829 }
830 }
831 }
832
833 if let Some(b) = get_enclosing_block(cx, local_id) {
834 let mut v = V {
835 cx,
836 local_id,
837 res: ControlFlow::Continue(()),
838 f,
839 };
840 v.visit_block(b);
841 v.res
842 } else {
843 ControlFlow::Continue(())
844 }
845}
846
847pub fn contains_break_or_continue(expr: &Expr<'_>) -> bool {
851 for_each_expr_without_closures(expr, |e| {
852 if matches!(e.kind, ExprKind::Break(..) | ExprKind::Continue(..)) {
853 ControlFlow::Break(())
854 } else {
855 ControlFlow::Continue(())
856 }
857 })
858 .is_some()
859}
860
861pub fn local_used_once<'tcx>(
864 cx: &LateContext<'tcx>,
865 visitable: impl Visitable<'tcx>,
866 id: HirId,
867) -> Option<&'tcx Expr<'tcx>> {
868 let mut expr = None;
869
870 let cf = for_each_expr(cx.tcx, visitable, |e| {
871 if e.res_local_id() == Some(id) && expr.replace(e).is_some() {
872 ControlFlow::Break(())
873 } else {
874 ControlFlow::Continue(())
875 }
876 });
877 if cf.is_some() {
878 return None;
879 }
880
881 expr
882}