1 use crate::path_to_local_id
;
2 use core
::ops
::ControlFlow
;
4 use rustc_hir
::def
::{DefKind, Res}
;
5 use rustc_hir
::intravisit
::{self, walk_block, walk_expr, Visitor}
;
7 Arm
, Block
, BlockCheckMode
, Body
, BodyId
, Expr
, ExprKind
, HirId
, ItemId
, ItemKind
, Stmt
, UnOp
, UnsafeSource
,
10 use rustc_lint
::LateContext
;
11 use rustc_middle
::hir
::map
::Map
;
12 use rustc_middle
::hir
::nested_filter
;
15 /// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
16 /// bodies (i.e. closures) are visited.
17 /// If the callback returns `true`, the expr just provided to the callback is walked.
19 pub fn expr_visitor
<'tcx
>(cx
: &LateContext
<'tcx
>, f
: impl FnMut(&'tcx Expr
<'tcx
>) -> bool
) -> impl Visitor
<'tcx
> {
24 impl<'tcx
, F
: FnMut(&'tcx Expr
<'tcx
>) -> bool
> Visitor
<'tcx
> for V
<'tcx
, F
> {
25 type NestedFilter
= nested_filter
::OnlyBodies
;
26 fn nested_visit_map(&mut self) -> Self::Map
{
30 fn visit_expr(&mut self, expr
: &'tcx Expr
<'tcx
>) {
32 walk_expr(self, expr
);
36 V { hir: cx.tcx.hir(), f }
39 /// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
40 /// bodies (i.e. closures) are not visited.
41 /// If the callback returns `true`, the expr just provided to the callback is walked.
43 pub fn expr_visitor_no_bodies
<'tcx
>(f
: impl FnMut(&'tcx Expr
<'tcx
>) -> bool
) -> impl Visitor
<'tcx
> {
45 impl<'tcx
, F
: FnMut(&'tcx Expr
<'tcx
>) -> bool
> Visitor
<'tcx
> for V
<F
> {
46 fn visit_expr(&mut self, e
: &'tcx Expr
<'_
>) {
55 /// returns `true` if expr contains match expr desugared from try
56 fn contains_try(expr
: &hir
::Expr
<'_
>) -> bool
{
57 let mut found
= false;
58 expr_visitor_no_bodies(|e
| {
60 found
= matches
!(e
.kind
, hir
::ExprKind
::Match(_
, _
, hir
::MatchSource
::TryDesugar
));
68 pub fn find_all_ret_expressions
<'hir
, F
>(_cx
: &LateContext
<'_
>, expr
: &'hir hir
::Expr
<'hir
>, callback
: F
) -> bool
70 F
: FnMut(&'hir hir
::Expr
<'hir
>) -> bool
,
78 struct WithStmtGuarg
<'a
, F
> {
79 val
: &'a
mut RetFinder
<F
>,
83 impl<F
> RetFinder
<F
> {
84 fn inside_stmt(&mut self, in_stmt
: bool
) -> WithStmtGuarg
<'_
, F
> {
85 let prev_in_stmt
= std
::mem
::replace(&mut self.in_stmt
, in_stmt
);
93 impl<F
> std
::ops
::Deref
for WithStmtGuarg
<'_
, F
> {
94 type Target
= RetFinder
<F
>;
96 fn deref(&self) -> &Self::Target
{
101 impl<F
> std
::ops
::DerefMut
for WithStmtGuarg
<'_
, F
> {
102 fn deref_mut(&mut self) -> &mut Self::Target
{
107 impl<F
> Drop
for WithStmtGuarg
<'_
, F
> {
109 self.val
.in_stmt
= self.prev_in_stmt
;
113 impl<'hir
, F
: FnMut(&'hir hir
::Expr
<'hir
>) -> bool
> intravisit
::Visitor
<'hir
> for RetFinder
<F
> {
114 fn visit_stmt(&mut self, stmt
: &'hir hir
::Stmt
<'_
>) {
115 intravisit
::walk_stmt(&mut *self.inside_stmt(true), stmt
);
118 fn visit_expr(&mut self, expr
: &'hir hir
::Expr
<'_
>) {
124 hir
::ExprKind
::Ret(Some(expr
)) => self.inside_stmt(false).visit_expr(expr
),
125 _
=> intravisit
::walk_expr(self, expr
),
129 hir
::ExprKind
::If(cond
, then
, else_opt
) => {
130 self.inside_stmt(true).visit_expr(cond
);
131 self.visit_expr(then
);
132 if let Some(el
) = else_opt
{
136 hir
::ExprKind
::Match(cond
, arms
, _
) => {
137 self.inside_stmt(true).visit_expr(cond
);
139 self.visit_expr(arm
.body
);
142 hir
::ExprKind
::Block(..) => intravisit
::walk_expr(self, expr
),
143 hir
::ExprKind
::Ret(Some(expr
)) => self.visit_expr(expr
),
144 _
=> self.failed
|= !(self.cb
)(expr
),
150 !contains_try(expr
) && {
151 let mut ret_finder
= RetFinder
{
156 ret_finder
.visit_expr(expr
);
161 /// A type which can be visited.
162 pub trait Visitable
<'tcx
> {
163 /// Calls the corresponding `visit_*` function on the visitor.
164 fn visit
<V
: Visitor
<'tcx
>>(self, visitor
: &mut V
);
166 macro_rules
! visitable_ref
{
167 ($t
:ident
, $f
:ident
) => {
168 impl<'tcx
> Visitable
<'tcx
> for &'tcx $t
<'tcx
> {
169 fn visit
<V
: Visitor
<'tcx
>>(self, visitor
: &mut V
) {
175 visitable_ref
!(Arm
, visit_arm
);
176 visitable_ref
!(Block
, visit_block
);
177 visitable_ref
!(Body
, visit_body
);
178 visitable_ref
!(Expr
, visit_expr
);
179 visitable_ref
!(Stmt
, visit_stmt
);
181 // impl<'tcx, I: IntoIterator> Visitable<'tcx> for I
183 // I::Item: Visitable<'tcx>,
185 // fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
192 /// Checks if the given resolved path is used in the given body.
193 pub fn is_res_used(cx
: &LateContext
<'_
>, res
: Res
, body
: BodyId
) -> bool
{
194 let mut found
= false;
195 expr_visitor(cx
, |e
| {
200 if let ExprKind
::Path(p
) = &e
.kind
{
201 if cx
.qpath_res(p
, e
.hir_id
) == res
{
207 .visit_expr(&cx
.tcx
.hir().body(body
).value
);
211 /// Checks if the given local is used.
212 pub fn is_local_used
<'tcx
>(cx
: &LateContext
<'tcx
>, visitable
: impl Visitable
<'tcx
>, id
: HirId
) -> bool
{
213 let mut is_used
= false;
214 let mut visitor
= expr_visitor(cx
, |expr
| {
216 is_used
= path_to_local_id(expr
, id
);
220 visitable
.visit(&mut visitor
);
225 /// Checks if the given expression is a constant.
226 pub fn is_const_evaluatable
<'tcx
>(cx
: &LateContext
<'tcx
>, e
: &'tcx Expr
<'_
>) -> bool
{
228 cx
: &'a LateContext
<'tcx
>,
231 impl<'tcx
> Visitor
<'tcx
> for V
<'_
, 'tcx
> {
232 type NestedFilter
= nested_filter
::OnlyBodies
;
233 fn nested_visit_map(&mut self) -> Self::Map
{
237 fn visit_expr(&mut self, e
: &'tcx Expr
<'_
>) {
242 ExprKind
::ConstBlock(_
) => return,
245 kind
: ExprKind
::Path(ref p
),
252 .qpath_res(p
, hir_id
)
254 .map_or(false, |id
| self.cx
.tcx
.is_const_fn_raw(id
)) => {}
,
255 ExprKind
::MethodCall(..)
259 .type_dependent_def_id(e
.hir_id
)
260 .map_or(false, |id
| self.cx
.tcx
.is_const_fn_raw(id
)) => {}
,
261 ExprKind
::Binary(_
, lhs
, rhs
)
262 if self.cx
.typeck_results().expr_ty(lhs
).peel_refs().is_primitive_ty()
263 && self.cx
.typeck_results().expr_ty(rhs
).peel_refs().is_primitive_ty() => {}
,
264 ExprKind
::Unary(UnOp
::Deref
, e
) if self.cx
.typeck_results().expr_ty(e
).is_ref() => (),
265 ExprKind
::Unary(_
, e
) if self.cx
.typeck_results().expr_ty(e
).peel_refs().is_primitive_ty() => (),
266 ExprKind
::Index(base
, _
)
268 self.cx
.typeck_results().expr_ty(base
).peel_refs().kind(),
269 ty
::Slice(_
) | ty
::Array(..)
271 ExprKind
::Path(ref p
)
273 self.cx
.qpath_res(p
, e
.hir_id
),
276 | DefKind
::AssocConst
278 | DefKind
::ConstParam
288 | ExprKind
::Block(..)
290 | ExprKind
::DropTemps(_
)
291 | ExprKind
::Field(..)
295 | ExprKind
::Match(..)
296 | ExprKind
::Repeat(..)
297 | ExprKind
::Struct(..)
299 | ExprKind
::Type(..) => (),
302 self.is_const
= false;
310 let mut v
= V { cx, is_const: true }
;
315 /// Checks if the given expression performs an unsafe operation outside of an unsafe block.
316 pub fn is_expr_unsafe
<'tcx
>(cx
: &LateContext
<'tcx
>, e
: &'tcx Expr
<'_
>) -> bool
{
318 cx
: &'a LateContext
<'tcx
>,
321 impl<'tcx
> Visitor
<'tcx
> for V
<'_
, 'tcx
> {
322 type NestedFilter
= nested_filter
::OnlyBodies
;
323 fn nested_visit_map(&mut self) -> Self::Map
{
326 fn visit_expr(&mut self, e
: &'tcx Expr
<'_
>) {
331 ExprKind
::Unary(UnOp
::Deref
, e
) if self.cx
.typeck_results().expr_ty(e
).is_unsafe_ptr() => {
332 self.is_unsafe
= true;
334 ExprKind
::MethodCall(..)
338 .type_dependent_def_id(e
.hir_id
)
339 .map_or(false, |id
| self.cx
.tcx
.fn_sig(id
).unsafety() == Unsafety
::Unsafe
) =>
341 self.is_unsafe
= true;
343 ExprKind
::Call(func
, _
) => match *self.cx
.typeck_results().expr_ty(func
).peel_refs().kind() {
344 ty
::FnDef(id
, _
) if self.cx
.tcx
.fn_sig(id
).unsafety() == Unsafety
::Unsafe
=> self.is_unsafe
= true,
345 ty
::FnPtr(sig
) if sig
.unsafety() == Unsafety
::Unsafe
=> self.is_unsafe
= true,
346 _
=> walk_expr(self, e
),
348 ExprKind
::Path(ref p
)
351 .qpath_res(p
, e
.hir_id
)
353 .map_or(false, |id
| self.cx
.tcx
.is_mutable_static(id
)) =>
355 self.is_unsafe
= true;
357 _
=> walk_expr(self, e
),
360 fn visit_block(&mut self, b
: &'tcx Block
<'_
>) {
361 if !matches
!(b
.rules
, BlockCheckMode
::UnsafeBlock(_
)) {
365 fn visit_nested_item(&mut self, id
: ItemId
) {
366 if let ItemKind
::Impl(i
) = &self.cx
.tcx
.hir().item(id
).kind
{
367 self.is_unsafe
= i
.unsafety
== Unsafety
::Unsafe
;
371 let mut v
= V { cx, is_unsafe: false }
;
376 /// Checks if the given expression contains an unsafe block
377 pub fn contains_unsafe_block
<'tcx
>(cx
: &LateContext
<'tcx
>, e
: &'tcx Expr
<'tcx
>) -> bool
{
378 struct V
<'cx
, 'tcx
> {
379 cx
: &'cx LateContext
<'tcx
>,
382 impl<'tcx
> Visitor
<'tcx
> for V
<'_
, 'tcx
> {
383 type NestedFilter
= nested_filter
::OnlyBodies
;
384 fn nested_visit_map(&mut self) -> Self::Map
{
388 fn visit_block(&mut self, b
: &'tcx Block
<'_
>) {
389 if self.found_unsafe
{
392 if b
.rules
== BlockCheckMode
::UnsafeBlock(UnsafeSource
::UserProvided
) {
393 self.found_unsafe
= true;
407 /// Runs the given function for each sub-expression producing the final value consumed by the parent
408 /// of the give expression.
410 /// e.g. for the following expression
418 /// this will pass both `f(0)` and `1+1` to the given function.
419 pub fn for_each_value_source
<'tcx
, B
>(
421 f
: &mut impl FnMut(&'tcx Expr
<'tcx
>) -> ControlFlow
<B
>,
422 ) -> ControlFlow
<B
> {
424 ExprKind
::Block(Block { expr: Some(e), .. }
, _
) => for_each_value_source(e
, f
),
425 ExprKind
::Match(_
, arms
, _
) => {
427 for_each_value_source(arm
.body
, f
)?
;
429 ControlFlow
::Continue(())
431 ExprKind
::If(_
, if_expr
, Some(else_expr
)) => {
432 for_each_value_source(if_expr
, f
)?
;
433 for_each_value_source(else_expr
, f
)
435 ExprKind
::DropTemps(e
) => for_each_value_source(e
, f
),