1 use crate::ty::needs_ordered_drop;
2 use crate::{get_enclosing_block, path_to_local_id};
3 use core::ops::ControlFlow;
5 use rustc_hir::def::{CtorKind, DefKind, Res};
6 use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
8 AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, Pat, QPath,
9 Stmt, UnOp, UnsafeSource, Unsafety,
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};
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.
21 fn descend(&self) -> bool;
24 use internal::Continue;
26 impl Continue for () {
27 fn descend(&self) -> bool {
32 /// Allows for controlled descent when using visitor functions. Use `()` instead when always
33 /// descending into child nodes.
34 #[derive(Clone, Copy)]
39 impl From<bool> for Descend {
40 fn from(from: bool) -> Self {
41 if from { Self::Yes } else { Self::No }
44 impl Continue for Descend {
45 fn descend(&self) -> bool {
46 matches!(self, Self::Yes)
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);
55 macro_rules! visitable_ref {
56 ($t:ident, $f:ident) => {
57 impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> {
58 fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
64 visitable_ref!(Arm, visit_arm);
65 visitable_ref!(Block, visit_block);
66 visitable_ref!(Body, visit_body);
67 visitable_ref!(Expr, visit_expr);
68 visitable_ref!(Stmt, visit_stmt);
70 /// Calls the given function once for each expression contained. This does not enter any bodies or
72 pub fn for_each_expr<'tcx, B, C: Continue>(
73 node: impl Visitable<'tcx>,
74 f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
80 impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<B, F> {
81 fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
82 if self.res.is_some() {
86 ControlFlow::Continue(c) if c.descend() => walk_expr(self, e),
87 ControlFlow::Break(b) => self.res = Some(b),
88 ControlFlow::Continue(_) => (),
92 // Avoid unnecessary `walk_*` calls.
93 fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) {}
94 fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {}
95 fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {}
96 // Avoid monomorphising all `visit_*` functions.
97 fn visit_nested_item(&mut self, _: ItemId) {}
99 let mut v = V { f, res: None };
104 /// Calls the given function once for each expression contained. This will enter bodies, but not
106 pub fn for_each_expr_with_closures<'tcx, B, C: Continue>(
107 cx: &LateContext<'tcx>,
108 node: impl Visitable<'tcx>,
109 f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
111 struct V<'tcx, B, F> {
116 impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<'tcx, B, F> {
117 type NestedFilter = nested_filter::OnlyBodies;
118 fn nested_visit_map(&mut self) -> Self::Map {
122 fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
123 if self.res.is_some() {
127 ControlFlow::Continue(c) if c.descend() => walk_expr(self, e),
128 ControlFlow::Break(b) => self.res = Some(b),
129 ControlFlow::Continue(_) => (),
133 // Only walk closures
134 fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
135 // Avoid unnecessary `walk_*` calls.
136 fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) {}
137 fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {}
138 fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {}
139 // Avoid monomorphising all `visit_*` functions.
140 fn visit_nested_item(&mut self, _: ItemId) {}
151 /// returns `true` if expr contains match expr desugared from try
152 fn contains_try(expr: &hir::Expr<'_>) -> bool {
153 for_each_expr(expr, |e| {
154 if matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar)) {
155 ControlFlow::Break(())
157 ControlFlow::Continue(())
163 pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool
165 F: FnMut(&'hir hir::Expr<'hir>) -> bool,
167 struct RetFinder<F> {
173 struct WithStmtGuarg<'a, F> {
174 val: &'a mut RetFinder<F>,
178 impl<F> RetFinder<F> {
179 fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
180 let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
188 impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
189 type Target = RetFinder<F>;
191 fn deref(&self) -> &Self::Target {
196 impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
197 fn deref_mut(&mut self) -> &mut Self::Target {
202 impl<F> Drop for WithStmtGuarg<'_, F> {
204 self.val.in_stmt = self.prev_in_stmt;
208 impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder<F> {
209 fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) {
210 intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt);
213 fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) {
219 hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr),
220 _ => intravisit::walk_expr(self, expr),
224 hir::ExprKind::If(cond, then, else_opt) => {
225 self.inside_stmt(true).visit_expr(cond);
226 self.visit_expr(then);
227 if let Some(el) = else_opt {
231 hir::ExprKind::Match(cond, arms, _) => {
232 self.inside_stmt(true).visit_expr(cond);
234 self.visit_expr(arm.body);
237 hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr),
238 hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr),
239 _ => self.failed |= !(self.cb)(expr),
245 !contains_try(expr) && {
246 let mut ret_finder = RetFinder {
251 ret_finder.visit_expr(expr);
256 /// Checks if the given resolved path is used in the given body.
257 pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
258 for_each_expr_with_closures(cx, cx.tcx.hir().body(body).value, |e| {
259 if let ExprKind::Path(p) = &e.kind {
260 if cx.qpath_res(p, e.hir_id) == res {
261 return ControlFlow::Break(());
264 ControlFlow::Continue(())
269 /// Checks if the given local is used.
270 pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
271 for_each_expr_with_closures(cx, visitable, |e| {
272 if path_to_local_id(e, id) {
273 ControlFlow::Break(())
275 ControlFlow::Continue(())
281 /// Checks if the given expression is a constant.
282 pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
284 cx: &'a LateContext<'tcx>,
287 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
288 type NestedFilter = nested_filter::OnlyBodies;
289 fn nested_visit_map(&mut self) -> Self::Map {
293 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
298 ExprKind::ConstBlock(_) => return,
301 kind: ExprKind::Path(ref p),
308 .qpath_res(p, hir_id)
310 .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
311 ExprKind::MethodCall(..)
315 .type_dependent_def_id(e.hir_id)
316 .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
317 ExprKind::Binary(_, lhs, rhs)
318 if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty()
319 && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {},
320 ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_ref() => (),
321 ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (),
322 ExprKind::Index(base, _)
324 self.cx.typeck_results().expr_ty(base).peel_refs().kind(),
325 ty::Slice(_) | ty::Array(..)
327 ExprKind::Path(ref p)
329 self.cx.qpath_res(p, e.hir_id),
332 | DefKind::AssocConst
334 | DefKind::ConstParam
344 | ExprKind::Block(..)
346 | ExprKind::DropTemps(_)
347 | ExprKind::Field(..)
351 | ExprKind::Match(..)
352 | ExprKind::Repeat(..)
353 | ExprKind::Struct(..)
355 | ExprKind::Type(..) => (),
358 self.is_const = false;
366 let mut v = V { cx, is_const: true };
371 /// Checks if the given expression performs an unsafe operation outside of an unsafe block.
372 pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
374 cx: &'a LateContext<'tcx>,
377 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
378 type NestedFilter = nested_filter::OnlyBodies;
379 fn nested_visit_map(&mut self) -> Self::Map {
382 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
387 ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => {
388 self.is_unsafe = true;
390 ExprKind::MethodCall(..)
394 .type_dependent_def_id(e.hir_id)
395 .map_or(false, |id| self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe) =>
397 self.is_unsafe = true;
399 ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
400 ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
401 ty::FnPtr(sig) if sig.unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
402 _ => walk_expr(self, e),
404 ExprKind::Path(ref p)
407 .qpath_res(p, e.hir_id)
409 .map_or(false, |id| self.cx.tcx.is_mutable_static(id)) =>
411 self.is_unsafe = true;
413 _ => walk_expr(self, e),
416 fn visit_block(&mut self, b: &'tcx Block<'_>) {
417 if !matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
421 fn visit_nested_item(&mut self, id: ItemId) {
422 if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind {
423 self.is_unsafe = i.unsafety == Unsafety::Unsafe;
427 let mut v = V { cx, is_unsafe: false };
432 /// Checks if the given expression contains an unsafe block
433 pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
434 struct V<'cx, 'tcx> {
435 cx: &'cx LateContext<'tcx>,
438 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
439 type NestedFilter = nested_filter::OnlyBodies;
440 fn nested_visit_map(&mut self) -> Self::Map {
444 fn visit_block(&mut self, b: &'tcx Block<'_>) {
445 if self.found_unsafe {
448 if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
449 self.found_unsafe = true;
463 /// Runs the given function for each sub-expression producing the final value consumed by the parent
464 /// of the give expression.
466 /// e.g. for the following expression
474 /// this will pass both `f(0)` and `1+1` to the given function.
475 pub fn for_each_value_source<'tcx, B>(
477 f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
478 ) -> ControlFlow<B> {
480 ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f),
481 ExprKind::Match(_, arms, _) => {
483 for_each_value_source(arm.body, f)?;
485 ControlFlow::Continue(())
487 ExprKind::If(_, if_expr, Some(else_expr)) => {
488 for_each_value_source(if_expr, f)?;
489 for_each_value_source(else_expr, f)
491 ExprKind::DropTemps(e) => for_each_value_source(e, f),
496 /// Runs the given function for each path expression referencing the given local which occur after
497 /// the given expression.
498 pub fn for_each_local_use_after_expr<'tcx, B>(
499 cx: &LateContext<'tcx>,
502 f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
503 ) -> ControlFlow<B> {
504 struct V<'cx, 'tcx, F, B> {
505 cx: &'cx LateContext<'tcx>,
512 impl<'cx, 'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'cx, 'tcx, F, B> {
513 type NestedFilter = nested_filter::OnlyBodies;
514 fn nested_visit_map(&mut self) -> Self::Map {
518 fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
520 if e.hir_id == self.expr_id {
527 if self.res.is_break() {
530 if path_to_local_id(e, self.local_id) {
531 self.res = (self.f)(e);
538 if let Some(b) = get_enclosing_block(cx, local_id) {
544 res: ControlFlow::Continue(()),
550 ControlFlow::Continue(())
554 // Calls the given function for every unconsumed temporary created by the expression. Note the
555 // function is only guaranteed to be called for types which need to be dropped, but it may be called
557 #[allow(clippy::too_many_lines)]
558 pub fn for_each_unconsumed_temporary<'tcx, B>(
559 cx: &LateContext<'tcx>,
561 mut f: impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
562 ) -> ControlFlow<B> {
563 // Todo: Handle partially consumed values.
565 typeck: &'tcx TypeckResults<'tcx>,
568 f: &mut impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
569 ) -> ControlFlow<B> {
572 typeck.expr_adjustments(e),
573 [adjust, ..] if matches!(adjust.kind, Adjust::Borrow(_) | Adjust::Deref(_))
577 ExprKind::Path(QPath::Resolved(None, p))
578 if matches!(p.res, Res::Def(DefKind::Ctor(_, CtorKind::Const), _)) =>
580 f(typeck.expr_ty(e))?;
583 | ExprKind::Unary(UnOp::Deref, _)
584 | ExprKind::Index(..)
585 | ExprKind::Field(..)
586 | ExprKind::AddrOf(..) => (),
587 _ => f(typeck.expr_ty(e))?,
591 ExprKind::AddrOf(_, _, e)
592 | ExprKind::Field(e, _)
593 | ExprKind::Unary(UnOp::Deref, e)
594 | ExprKind::Match(e, ..)
595 | ExprKind::Let(&Let { init: e, .. }) => {
596 helper(typeck, false, e, f)?;
598 ExprKind::Block(&Block { expr: Some(e), .. }, _)
600 | ExprKind::Cast(e, _)
601 | ExprKind::Unary(_, e) => {
602 helper(typeck, true, e, f)?;
604 ExprKind::Call(callee, args) => {
605 helper(typeck, true, callee, f)?;
607 helper(typeck, true, arg, f)?;
610 ExprKind::MethodCall(_, receiver, args, _) => {
611 helper(typeck, true, receiver, f)?;
613 helper(typeck, true, arg, f)?;
616 ExprKind::Tup(args) | ExprKind::Array(args) => {
618 helper(typeck, true, arg, f)?;
621 ExprKind::Index(borrowed, consumed)
622 | ExprKind::Assign(borrowed, consumed, _)
623 | ExprKind::AssignOp(_, borrowed, consumed) => {
624 helper(typeck, false, borrowed, f)?;
625 helper(typeck, true, consumed, f)?;
627 ExprKind::Binary(_, lhs, rhs) => {
628 helper(typeck, true, lhs, f)?;
629 helper(typeck, true, rhs, f)?;
631 ExprKind::Struct(_, fields, default) => {
632 for field in fields {
633 helper(typeck, true, field.expr, f)?;
635 if let Some(default) = default {
636 helper(typeck, false, default, f)?;
639 ExprKind::If(cond, then, else_expr) => {
640 helper(typeck, true, cond, f)?;
641 helper(typeck, true, then, f)?;
642 if let Some(else_expr) = else_expr {
643 helper(typeck, true, else_expr, f)?;
646 ExprKind::Type(e, _) => {
647 helper(typeck, consume, e, f)?;
650 // Either drops temporaries, jumps out of the current expression, or has no sub expression.
651 ExprKind::DropTemps(_)
653 | ExprKind::Break(..)
654 | ExprKind::Yield(..)
655 | ExprKind::Block(..)
657 | ExprKind::Repeat(..)
659 | ExprKind::ConstBlock(_)
660 | ExprKind::Closure { .. }
662 | ExprKind::Continue(_)
663 | ExprKind::InlineAsm(_)
664 | ExprKind::Err => (),
666 ControlFlow::Continue(())
668 helper(cx.typeck_results(), true, e, &mut f)
671 pub fn any_temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
672 for_each_unconsumed_temporary(cx, e, |ty| {
673 if needs_ordered_drop(cx, ty) {
674 ControlFlow::Break(())
676 ControlFlow::Continue(())
682 /// Runs the given function for each path expression referencing the given local which occur after
683 /// the given expression.
684 pub fn for_each_local_assignment<'tcx, B>(
685 cx: &LateContext<'tcx>,
687 f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
688 ) -> ControlFlow<B> {
689 struct V<'cx, 'tcx, F, B> {
690 cx: &'cx LateContext<'tcx>,
695 impl<'cx, 'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'cx, 'tcx, F, B> {
696 type NestedFilter = nested_filter::OnlyBodies;
697 fn nested_visit_map(&mut self) -> Self::Map {
701 fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
702 if let ExprKind::Assign(lhs, rhs, _) = e.kind
703 && self.res.is_continue()
704 && path_to_local_id(lhs, self.local_id)
706 self.res = (self.f)(rhs);
707 self.visit_expr(rhs);
714 if let Some(b) = get_enclosing_block(cx, local_id) {
718 res: ControlFlow::Continue(()),
724 ControlFlow::Continue(())