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
, LetExpr
, Pat
, QPath
,
9 Safety
, Stmt
, UnOp
, UnsafeSource
,
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 impl<'tcx
, T
> Visitable
<'tcx
> for &'tcx
[T
]
57 &'tcx T
: Visitable
<'tcx
>,
59 fn visit
<V
: Visitor
<'tcx
>>(self, visitor
: &mut V
) {
65 impl<'tcx
, A
, B
> Visitable
<'tcx
> for (A
, B
)
70 fn visit
<V
: Visitor
<'tcx
>>(self, visitor
: &mut V
) {
76 impl<'tcx
, T
> Visitable
<'tcx
> for Option
<T
>
80 fn visit
<V
: Visitor
<'tcx
>>(self, visitor
: &mut V
) {
81 if let Some(x
) = self {
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
) {
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
);
101 /// Calls the given function once for each expression contained. This does not enter any bodies or
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
>,
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() {
117 ControlFlow
::Continue(c
) if c
.descend() => walk_expr(self, e
),
118 ControlFlow
::Break(b
) => self.res
= Some(b
),
119 ControlFlow
::Continue(_
) => (),
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
) {}
130 let mut v
= V { f, res: None }
;
135 /// Calls the given function once for each expression contained. This will enter bodies, but not
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
>,
142 struct V
<'tcx
, B
, F
> {
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
{
153 fn visit_expr(&mut self, e
: &'tcx Expr
<'tcx
>) {
154 if self.res
.is_some() {
158 ControlFlow
::Continue(c
) if c
.descend() => walk_expr(self, e
),
159 ControlFlow
::Break(b
) => self.res
= Some(b
),
160 ControlFlow
::Continue(_
) => (),
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
) {}
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(())
188 ControlFlow
::Continue(())
194 pub fn find_all_ret_expressions
<'hir
, F
>(_cx
: &LateContext
<'_
>, expr
: &'hir Expr
<'hir
>, callback
: F
) -> bool
196 F
: FnMut(&'hir Expr
<'hir
>) -> bool
,
198 struct RetFinder
<F
> {
204 struct WithStmtGuard
<'a
, F
> {
205 val
: &'a
mut RetFinder
<F
>,
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
);
219 impl<F
> std
::ops
::Deref
for WithStmtGuard
<'_
, F
> {
220 type Target
= RetFinder
<F
>;
222 fn deref(&self) -> &Self::Target
{
227 impl<F
> std
::ops
::DerefMut
for WithStmtGuard
<'_
, F
> {
228 fn deref_mut(&mut self) -> &mut Self::Target
{
233 impl<F
> Drop
for WithStmtGuard
<'_
, F
> {
235 self.val
.in_stmt
= self.prev_in_stmt
;
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
);
244 fn visit_expr(&mut self, expr
: &'hir Expr
<'_
>) {
250 ExprKind
::Ret(Some(expr
)) => self.inside_stmt(false).visit_expr(expr
),
251 _
=> walk_expr(self, expr
),
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
{
262 ExprKind
::Match(cond
, arms
, _
) => {
263 self.inside_stmt(true).visit_expr(cond
);
265 self.visit_expr(arm
.body
);
268 ExprKind
::Block(..) => walk_expr(self, expr
),
269 ExprKind
::Ret(Some(expr
)) => self.visit_expr(expr
),
270 _
=> self.failed
|= !(self.cb
)(expr
),
276 !contains_try(expr
) && {
277 let mut ret_finder
= RetFinder
{
282 ret_finder
.visit_expr(expr
);
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(());
295 ControlFlow
::Continue(())
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(())
306 ControlFlow
::Continue(())
312 /// Checks if the given expression is a constant.
313 pub fn is_const_evaluatable
<'tcx
>(cx
: &LateContext
<'tcx
>, e
: &'tcx Expr
<'_
>) -> bool
{
315 cx
: &'a LateContext
<'tcx
>,
318 impl<'tcx
> Visitor
<'tcx
> for V
<'_
, 'tcx
> {
319 type NestedFilter
= intravisit
::nested_filter
::None
;
321 fn visit_expr(&mut self, e
: &'tcx Expr
<'_
>) {
326 ExprKind
::ConstBlock(_
) => return,
329 kind
: ExprKind
::Path(ref p
),
336 .qpath_res(p
, hir_id
)
338 .map_or(false, |id
| self.cx
.tcx
.is_const_fn_raw(id
)) => {}
,
339 ExprKind
::MethodCall(..)
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
, _
, _
)
352 self.cx
.typeck_results().expr_ty(base
).peel_refs().kind(),
353 ty
::Slice(_
) | ty
::Array(..)
355 ExprKind
::Path(ref p
)
357 self.cx
.qpath_res(p
, e
.hir_id
),
360 | DefKind
::AssocConst
362 | DefKind
::ConstParam
372 | ExprKind
::Block(..)
374 | ExprKind
::DropTemps(_
)
375 | ExprKind
::Field(..)
379 | ExprKind
::Match(..)
380 | ExprKind
::Repeat(..)
381 | ExprKind
::Struct(..)
383 | ExprKind
::Type(..) => (),
386 self.is_const
= false;
394 let mut v
= V { cx, is_const: true }
;
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
{
402 cx
: &'a LateContext
<'tcx
>,
405 impl<'tcx
> Visitor
<'tcx
> for V
<'_
, 'tcx
> {
406 type NestedFilter
= nested_filter
::OnlyBodies
;
407 fn nested_visit_map(&mut self) -> Self::Map
{
410 fn visit_expr(&mut self, e
: &'tcx Expr
<'_
>) {
415 ExprKind
::Unary(UnOp
::Deref
, e
) if self.cx
.typeck_results().expr_ty(e
).is_unsafe_ptr() => {
416 self.is_unsafe
= true;
418 ExprKind
::MethodCall(..)
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
427 self.is_unsafe
= true;
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;
433 ty
::FnPtr(sig
) if sig
.safety() == Safety
::Unsafe
=> self.is_unsafe
= true,
434 _
=> walk_expr(self, e
),
436 ExprKind
::Path(ref p
)
439 .qpath_res(p
, e
.hir_id
)
441 .map_or(false, |id
| self.cx
.tcx
.is_mutable_static(id
)) =>
443 self.is_unsafe
= true;
445 _
=> walk_expr(self, e
),
448 fn visit_block(&mut self, b
: &'tcx Block
<'_
>) {
449 if !matches
!(b
.rules
, BlockCheckMode
::UnsafeBlock(_
)) {
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
;
459 let mut v
= V { cx, is_unsafe: false }
;
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
>,
470 impl<'tcx
> Visitor
<'tcx
> for V
<'_
, 'tcx
> {
471 type NestedFilter
= nested_filter
::OnlyBodies
;
472 fn nested_visit_map(&mut self) -> Self::Map
{
476 fn visit_block(&mut self, b
: &'tcx Block
<'_
>) {
477 if self.found_unsafe
{
480 if b
.rules
== BlockCheckMode
::UnsafeBlock(UnsafeSource
::UserProvided
) {
481 self.found_unsafe
= true;
495 /// Runs the given function for each sub-expression producing the final value consumed by the parent
496 /// of the give expression.
498 /// e.g. for the following expression
506 /// this will pass both `f(0)` and `1+1` to the given function.
507 pub fn for_each_value_source
<'tcx
, B
>(
509 f
: &mut impl FnMut(&'tcx Expr
<'tcx
>) -> ControlFlow
<B
>,
510 ) -> ControlFlow
<B
> {
512 ExprKind
::Block(Block { expr: Some(e), .. }
, _
) => for_each_value_source(e
, f
),
513 ExprKind
::Match(_
, arms
, _
) => {
515 for_each_value_source(arm
.body
, f
)?
;
517 ControlFlow
::Continue(())
519 ExprKind
::If(_
, if_expr
, Some(else_expr
)) => {
520 for_each_value_source(if_expr
, f
)?
;
521 for_each_value_source(else_expr
, f
)
523 ExprKind
::DropTemps(e
) => for_each_value_source(e
, f
),
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
>,
534 f
: impl FnMut(&'tcx Expr
<'tcx
>) -> ControlFlow
<B
>,
535 ) -> ControlFlow
<B
> {
536 struct V
<'cx
, 'tcx
, F
, B
> {
537 cx
: &'cx LateContext
<'tcx
>,
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
{
550 fn visit_expr(&mut self, e
: &'tcx Expr
<'tcx
>) {
552 if e
.hir_id
== self.expr_id
{
559 if self.res
.is_break() {
562 if path_to_local_id(e
, self.local_id
) {
563 self.res
= (self.f
)(e
);
570 if let Some(b
) = get_enclosing_block(cx
, local_id
) {
576 res
: ControlFlow
::Continue(()),
582 ControlFlow
::Continue(())
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
589 #[allow(clippy::too_many_lines)]
590 pub fn for_each_unconsumed_temporary
<'tcx
, B
>(
591 cx
: &LateContext
<'tcx
>,
593 mut f
: impl FnMut(Ty
<'tcx
>) -> ControlFlow
<B
>,
594 ) -> ControlFlow
<B
> {
595 // Todo: Handle partially consumed values.
597 typeck
: &'tcx TypeckResults
<'tcx
>,
600 f
: &mut impl FnMut(Ty
<'tcx
>) -> ControlFlow
<B
>,
601 ) -> ControlFlow
<B
> {
604 typeck
.expr_adjustments(e
),
605 [adjust
, ..] if matches
!(adjust
.kind
, Adjust
::Borrow(_
) | Adjust
::Deref(_
))
609 ExprKind
::Path(QPath
::Resolved(None
, p
))
610 if matches
!(p
.res
, Res
::Def(DefKind
::Ctor(_
, CtorKind
::Const
), _
)) =>
612 f(typeck
.expr_ty(e
))?
;
615 | ExprKind
::Unary(UnOp
::Deref
, _
)
616 | ExprKind
::Index(..)
617 | ExprKind
::Field(..)
618 | ExprKind
::AddrOf(..) => (),
619 _
=> f(typeck
.expr_ty(e
))?
,
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
)?
;
630 ExprKind
::Block(&Block { expr: Some(e), .. }
, _
) | ExprKind
::Cast(e
, _
) | ExprKind
::Unary(_
, e
) => {
631 helper(typeck
, true, e
, f
)?
;
633 ExprKind
::Call(callee
, args
) => {
634 helper(typeck
, true, callee
, f
)?
;
636 helper(typeck
, true, arg
, f
)?
;
639 ExprKind
::MethodCall(_
, receiver
, args
, _
) => {
640 helper(typeck
, true, receiver
, f
)?
;
642 helper(typeck
, true, arg
, f
)?
;
645 ExprKind
::Tup(args
) | ExprKind
::Array(args
) => {
647 helper(typeck
, true, arg
, f
)?
;
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
)?
;
656 ExprKind
::Binary(_
, lhs
, rhs
) => {
657 helper(typeck
, true, lhs
, f
)?
;
658 helper(typeck
, true, rhs
, f
)?
;
660 ExprKind
::Struct(_
, fields
, default) => {
661 for field
in fields
{
662 helper(typeck
, true, field
.expr
, f
)?
;
664 if let Some(default) = default {
665 helper(typeck
, false, default, f
)?
;
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
)?
;
675 ExprKind
::Type(e
, _
) => {
676 helper(typeck
, consume
, e
, f
)?
;
679 // Either drops temporaries, jumps out of the current expression, or has no sub expression.
680 ExprKind
::DropTemps(_
)
682 | ExprKind
::Become(_
)
683 | ExprKind
::Break(..)
684 | ExprKind
::Yield(..)
685 | ExprKind
::Block(..)
687 | ExprKind
::Repeat(..)
689 | ExprKind
::ConstBlock(_
)
690 | ExprKind
::Closure { .. }
692 | ExprKind
::Continue(_
)
693 | ExprKind
::InlineAsm(_
)
694 | ExprKind
::OffsetOf(..)
695 | ExprKind
::Err(_
) => (),
697 ControlFlow
::Continue(())
699 helper(cx
.typeck_results(), true, e
, &mut f
)
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(())
707 ControlFlow
::Continue(())
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
>,
718 f
: impl FnMut(&'tcx Expr
<'tcx
>) -> ControlFlow
<B
>,
719 ) -> ControlFlow
<B
> {
720 struct V
<'cx
, 'tcx
, F
, B
> {
721 cx
: &'cx LateContext
<'tcx
>,
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
{
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
)
737 self.res
= (self.f
)(rhs
);
738 self.visit_expr(rhs
);
745 if let Some(b
) = get_enclosing_block(cx
, local_id
) {
749 res
: ControlFlow
::Continue(()),
755 ControlFlow
::Continue(())
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(())
764 ControlFlow
::Continue(())
770 /// If the local is only used once in `visitable` returns the path expression referencing the given
772 pub fn local_used_once
<'tcx
>(
773 cx
: &LateContext
<'tcx
>,
774 visitable
: impl Visitable
<'tcx
>,
776 ) -> Option
<&'tcx Expr
<'tcx
>> {
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(())
783 ControlFlow
::Continue(())