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, 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
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
325fn is_const_evaluatable_helper<'tcx>(tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'tcx>, e: &'tcx Expr<'_>) -> bool {
328 match e.kind {
329 ExprKind::Call(
330 &Expr {
331 kind: ExprKind::Path(ref p),
332 hir_id,
333 ..
334 },
335 _,
336 ) if typeck
337 .qpath_res(p, hir_id)
338 .opt_def_id()
339 .is_some_and(|id| is_stable_const_fn_at(tcx, CRATE_HIR_ID, id, Msrv::default())) =>
340 {
341 true
342 },
343 ExprKind::MethodCall(..)
344 if typeck
345 .type_dependent_def_id(e.hir_id)
346 .is_some_and(|id| is_stable_const_fn_at(tcx, CRATE_HIR_ID, id, Msrv::default())) =>
347 {
348 true
349 },
350 ExprKind::Binary(_, lhs, rhs)
351 if typeck.expr_ty(lhs).peel_refs().is_primitive_ty()
352 && typeck.expr_ty(rhs).peel_refs().is_primitive_ty() =>
353 {
354 true
355 },
356 ExprKind::Unary(UnOp::Deref, e) if typeck.expr_ty(e).is_raw_ptr() => true,
357 ExprKind::Unary(_, e) if typeck.expr_ty(e).peel_refs().is_primitive_ty() => true,
358 ExprKind::Index(base, _, _)
359 if matches!(typeck.expr_ty(base).peel_refs().kind(), ty::Slice(_) | ty::Array(..)) =>
360 {
361 true
362 },
363 ExprKind::Path(ref p)
364 if matches!(
365 typeck.qpath_res(p, e.hir_id),
366 Res::Def(
367 DefKind::Const { .. }
368 | DefKind::AssocConst { .. }
369 | DefKind::AnonConst
370 | DefKind::ConstParam
371 | DefKind::Ctor(..)
372 | DefKind::Fn
373 | DefKind::AssocFn,
374 _
375 ) | Res::SelfCtor(_)
376 ) =>
377 {
378 true
379 },
380
381 ExprKind::AddrOf(..)
382 | ExprKind::Array(_)
383 | ExprKind::Block(..)
384 | ExprKind::Cast(..)
385 | ExprKind::ConstBlock(_)
386 | ExprKind::DropTemps(_)
387 | ExprKind::Field(..)
388 | ExprKind::If(..)
389 | ExprKind::Let(..)
390 | ExprKind::Lit(_)
391 | ExprKind::Match(..)
392 | ExprKind::Repeat(..)
393 | ExprKind::Struct(..)
394 | ExprKind::Tup(_)
395 | ExprKind::Type(..)
396 | ExprKind::UnsafeBinderCast(..) => true,
397
398 _ => false,
399 }
400}
401
402pub fn is_const_evaluatable<'tcx>(tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'tcx>, e: &'tcx Expr<'_>) -> bool {
404 for_each_expr(tcx, e, move |e| {
405 if !is_const_evaluatable_helper(tcx, typeck, e) {
406 ControlFlow::Break(())
407 } else if matches!(e.kind, ExprKind::ConstBlock(_)) {
408 ControlFlow::Continue(Descend::No)
409 } else {
410 ControlFlow::Continue(Descend::Yes)
411 }
412 })
413 .is_none()
414}
415
416pub fn is_const_param_evaluatable<'tcx>(
421 tcx: TyCtxt<'tcx>,
422 typeck: &'tcx TypeckResults<'tcx>,
423 e: &'tcx Expr<'_>,
424) -> bool {
425 struct V1<'tcx> {
426 tcx: TyCtxt<'tcx>,
427 typeck: &'tcx TypeckResults<'tcx>,
428 }
429 impl<'tcx> Visitor<'tcx> for V1<'tcx> {
430 type NestedFilter = nested_filter::OnlyBodies;
431 type Result = ControlFlow<()>;
432 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
433 self.tcx
434 }
435 fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) -> Self::Result {
436 if !is_const_evaluatable_helper(self.tcx, self.typeck, e) {
437 return ControlFlow::Break(());
438 }
439 walk_expr(self, e)
440 }
441 fn visit_inline_const(&mut self, c: &'tcx ConstBlock) -> Self::Result {
442 V2(self.tcx).visit_inline_const(c)
443 }
444 fn visit_anon_const(&mut self, c: &'tcx AnonConst) -> Self::Result {
445 V2(self.tcx).visit_anon_const(c)
446 }
447 fn visit_qpath(&mut self, qpath: &'tcx QPath<'tcx>, id: HirId, _span: Span) -> Self::Result {
448 if matches!(qpath.basic_res(), Res::Def(DefKind::ConstParam | DefKind::TyParam, _)) {
449 return ControlFlow::Break(());
450 }
451 walk_qpath(self, qpath, id)
452 }
453 }
454
455 struct V2<'tcx>(TyCtxt<'tcx>);
456 impl<'tcx> Visitor<'tcx> for V2<'tcx> {
457 type NestedFilter = nested_filter::OnlyBodies;
458 type Result = ControlFlow<()>;
459 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
460 self.0
461 }
462 fn visit_qpath(&mut self, qpath: &'tcx QPath<'tcx>, id: HirId, _span: Span) -> Self::Result {
463 if matches!(qpath.basic_res(), Res::Def(DefKind::ConstParam | DefKind::TyParam, _)) {
464 return ControlFlow::Break(());
465 }
466 walk_qpath(self, qpath, id)
467 }
468 }
469
470 if let ExprKind::Path(ref qpath) = e.kind
476 && matches!(qpath.basic_res(), Res::Def(DefKind::ConstParam, _))
477 {
478 return true;
479 }
480
481 V1 { tcx, typeck }.visit_expr(e).is_continue()
482}
483
484pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
486 struct V<'a, 'tcx> {
487 cx: &'a LateContext<'tcx>,
488 }
489 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
490 type NestedFilter = nested_filter::OnlyBodies;
491 type Result = ControlFlow<()>;
492
493 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
494 self.cx.tcx
495 }
496 fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result {
497 match e.kind {
498 ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_raw_ptr() => {
499 ControlFlow::Break(())
500 },
501 ExprKind::MethodCall(..)
502 if self
503 .cx
504 .typeck_results()
505 .type_dependent_def_id(e.hir_id)
506 .is_some_and(|id| self.cx.tcx.fn_sig(id).skip_binder().safety().is_unsafe()) =>
507 {
508 ControlFlow::Break(())
509 },
510 ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
511 ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().safety().is_unsafe() => {
512 ControlFlow::Break(())
513 },
514 ty::FnPtr(_, hdr) if hdr.safety().is_unsafe() => ControlFlow::Break(()),
515 _ => walk_expr(self, e),
516 },
517 ExprKind::Path(ref p)
518 if self
519 .cx
520 .qpath_res(p, e.hir_id)
521 .opt_def_id()
522 .is_some_and(|id| self.cx.tcx.is_mutable_static(id)) =>
523 {
524 ControlFlow::Break(())
525 },
526 _ => walk_expr(self, e),
527 }
528 }
529 fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result {
530 if matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
531 ControlFlow::Continue(())
532 } else {
533 walk_block(self, b)
534 }
535 }
536 fn visit_nested_item(&mut self, id: ItemId) -> Self::Result {
537 if let ItemKind::Impl(i) = &self.cx.tcx.hir_item(id).kind
538 && let Some(of_trait) = i.of_trait
539 && of_trait.safety.is_unsafe()
540 {
541 ControlFlow::Break(())
542 } else {
543 ControlFlow::Continue(())
544 }
545 }
546 }
547 let mut v = V { cx };
548 v.visit_expr(e).is_break()
549}
550
551pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
553 struct V<'cx, 'tcx> {
554 cx: &'cx LateContext<'tcx>,
555 }
556 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
557 type Result = ControlFlow<()>;
558 type NestedFilter = nested_filter::OnlyBodies;
559 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
560 self.cx.tcx
561 }
562
563 fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result {
564 if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
565 ControlFlow::Break(())
566 } else {
567 walk_block(self, b)
568 }
569 }
570 }
571 let mut v = V { cx };
572 v.visit_expr(e).is_break()
573}
574
575pub fn for_each_value_source<'tcx, B>(
588 e: &'tcx Expr<'tcx>,
589 f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
590) -> ControlFlow<B> {
591 match e.kind {
592 ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f),
593 ExprKind::Match(_, arms, _) => {
594 for arm in arms {
595 for_each_value_source(arm.body, f)?;
596 }
597 ControlFlow::Continue(())
598 },
599 ExprKind::If(_, if_expr, Some(else_expr)) => {
600 for_each_value_source(if_expr, f)?;
601 for_each_value_source(else_expr, f)
602 },
603 ExprKind::DropTemps(e) => for_each_value_source(e, f),
604 _ => f(e),
605 }
606}
607
608pub fn for_each_local_use_after_expr<'tcx, B>(
611 cx: &LateContext<'tcx>,
612 local_id: HirId,
613 expr_id: HirId,
614 f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
615) -> ControlFlow<B> {
616 struct V<'cx, 'tcx, F, B> {
617 cx: &'cx LateContext<'tcx>,
618 local_id: HirId,
619 expr_id: HirId,
620 found: bool,
621 res: ControlFlow<B>,
622 f: F,
623 }
624 impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'_, 'tcx, F, B> {
625 type NestedFilter = nested_filter::OnlyBodies;
626 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
627 self.cx.tcx
628 }
629
630 fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
631 if !self.found {
632 if e.hir_id == self.expr_id {
633 self.found = true;
634 } else {
635 walk_expr(self, e);
636 }
637 return;
638 }
639 if self.res.is_break() {
640 return;
641 }
642 if e.res_local_id() == Some(self.local_id) {
643 self.res = (self.f)(e);
644 } else {
645 walk_expr(self, e);
646 }
647 }
648 }
649
650 if let Some(b) = get_enclosing_block(cx, local_id) {
651 let mut v = V {
652 cx,
653 local_id,
654 expr_id,
655 found: false,
656 res: ControlFlow::Continue(()),
657 f,
658 };
659 v.visit_block(b);
660 v.res
661 } else {
662 ControlFlow::Continue(())
663 }
664}
665
666#[expect(clippy::too_many_lines)]
670pub fn for_each_unconsumed_temporary<'tcx, B>(
671 cx: &LateContext<'tcx>,
672 e: &'tcx Expr<'tcx>,
673 mut f: impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
674) -> ControlFlow<B> {
675 fn helper<'tcx, B>(
677 typeck: &'tcx TypeckResults<'tcx>,
678 consume: bool,
679 e: &'tcx Expr<'tcx>,
680 f: &mut impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
681 ) -> ControlFlow<B> {
682 if !consume
683 || matches!(
684 typeck.expr_adjustments(e),
685 [adjust, ..] if matches!(adjust.kind, Adjust::Borrow(_) | Adjust::Deref(_))
686 )
687 {
688 match e.kind {
689 ExprKind::Path(QPath::Resolved(None, p))
690 if matches!(p.res, Res::Def(DefKind::Ctor(_, CtorKind::Const), _)) =>
691 {
692 f(typeck.expr_ty(e))?;
693 },
694 ExprKind::Path(_)
695 | ExprKind::Unary(UnOp::Deref, _)
696 | ExprKind::Index(..)
697 | ExprKind::Field(..)
698 | ExprKind::AddrOf(..) => (),
699 _ => f(typeck.expr_ty(e))?,
700 }
701 }
702 match e.kind {
703 ExprKind::AddrOf(_, _, e)
704 | ExprKind::Field(e, _)
705 | ExprKind::Unary(UnOp::Deref, e)
706 | ExprKind::Match(e, ..)
707 | ExprKind::Let(&LetExpr { init: e, .. }) => {
708 helper(typeck, false, e, f)?;
709 },
710 ExprKind::Block(&Block { expr: Some(e), .. }, _) | ExprKind::Cast(e, _) | ExprKind::Unary(_, e) => {
711 helper(typeck, true, e, f)?;
712 },
713 ExprKind::Call(callee, args) => {
714 helper(typeck, true, callee, f)?;
715 for arg in args {
716 helper(typeck, true, arg, f)?;
717 }
718 },
719 ExprKind::MethodCall(_, receiver, args, _) => {
720 helper(typeck, true, receiver, f)?;
721 for arg in args {
722 helper(typeck, true, arg, f)?;
723 }
724 },
725 ExprKind::Tup(args) | ExprKind::Array(args) => {
726 for arg in args {
727 helper(typeck, true, arg, f)?;
728 }
729 },
730 ExprKind::Use(expr, _) => {
731 helper(typeck, true, expr, f)?;
732 },
733 ExprKind::Index(borrowed, consumed, _)
734 | ExprKind::Assign(borrowed, consumed, _)
735 | ExprKind::AssignOp(_, borrowed, consumed) => {
736 helper(typeck, false, borrowed, f)?;
737 helper(typeck, true, consumed, f)?;
738 },
739 ExprKind::Binary(_, lhs, rhs) => {
740 helper(typeck, true, lhs, f)?;
741 helper(typeck, true, rhs, f)?;
742 },
743 ExprKind::Struct(_, fields, default) => {
744 for field in fields {
745 helper(typeck, true, field.expr, f)?;
746 }
747 if let StructTailExpr::Base(default) = default {
748 helper(typeck, false, default, f)?;
749 }
750 },
751 ExprKind::If(cond, then, else_expr) => {
752 helper(typeck, true, cond, f)?;
753 helper(typeck, true, then, f)?;
754 if let Some(else_expr) = else_expr {
755 helper(typeck, true, else_expr, f)?;
756 }
757 },
758 ExprKind::Type(e, _) | ExprKind::UnsafeBinderCast(_, e, _) => {
759 helper(typeck, consume, e, f)?;
760 },
761
762 ExprKind::DropTemps(_)
764 | ExprKind::Ret(_)
765 | ExprKind::Become(_)
766 | ExprKind::Break(..)
767 | ExprKind::Yield(..)
768 | ExprKind::Block(..)
769 | ExprKind::Loop(..)
770 | ExprKind::Repeat(..)
771 | ExprKind::Lit(_)
772 | ExprKind::ConstBlock(_)
773 | ExprKind::Closure { .. }
774 | ExprKind::Path(_)
775 | ExprKind::Continue(_)
776 | ExprKind::InlineAsm(_)
777 | ExprKind::OffsetOf(..)
778 | ExprKind::Err(_) => (),
779 }
780 ControlFlow::Continue(())
781 }
782 helper(cx.typeck_results(), true, e, &mut f)
783}
784
785pub fn any_temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
786 for_each_unconsumed_temporary(cx, e, |ty| {
787 if needs_ordered_drop(cx, ty) {
788 ControlFlow::Break(())
789 } else {
790 ControlFlow::Continue(())
791 }
792 })
793 .is_break()
794}
795
796pub fn for_each_local_assignment<'tcx, B>(
799 cx: &LateContext<'tcx>,
800 local_id: HirId,
801 f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
802) -> ControlFlow<B> {
803 struct V<'cx, 'tcx, F, B> {
804 cx: &'cx LateContext<'tcx>,
805 local_id: HirId,
806 res: ControlFlow<B>,
807 f: F,
808 }
809 impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'_, 'tcx, F, B> {
810 type NestedFilter = nested_filter::OnlyBodies;
811 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
812 self.cx.tcx
813 }
814
815 fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
816 if let ExprKind::Assign(lhs, rhs, _) = e.kind
817 && self.res.is_continue()
818 && lhs.res_local_id() == Some(self.local_id)
819 {
820 self.res = (self.f)(rhs);
821 self.visit_expr(rhs);
822 } else {
823 walk_expr(self, e);
824 }
825 }
826 }
827
828 if let Some(b) = get_enclosing_block(cx, local_id) {
829 let mut v = V {
830 cx,
831 local_id,
832 res: ControlFlow::Continue(()),
833 f,
834 };
835 v.visit_block(b);
836 v.res
837 } else {
838 ControlFlow::Continue(())
839 }
840}
841
842pub fn contains_break_or_continue(expr: &Expr<'_>) -> bool {
843 for_each_expr_without_closures(expr, |e| {
844 if matches!(e.kind, ExprKind::Break(..) | ExprKind::Continue(..)) {
845 ControlFlow::Break(())
846 } else {
847 ControlFlow::Continue(())
848 }
849 })
850 .is_some()
851}
852
853pub fn local_used_once<'tcx>(
856 cx: &LateContext<'tcx>,
857 visitable: impl Visitable<'tcx>,
858 id: HirId,
859) -> Option<&'tcx Expr<'tcx>> {
860 let mut expr = None;
861
862 let cf = for_each_expr(cx.tcx, visitable, |e| {
863 if e.res_local_id() == Some(id) && expr.replace(e).is_some() {
864 ControlFlow::Break(())
865 } else {
866 ControlFlow::Continue(())
867 }
868 });
869 if cf.is_some() {
870 return None;
871 }
872
873 expr
874}