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