]> git.lizzy.rs Git - rust.git/blob - clippy_utils/src/visitors.rs
Merge remote-tracking branch 'upstream/master' into rustup
[rust.git] / 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     Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, QPath, Stmt, UnOp,
9     UnsafeSource, Unsafety,
10 };
11 use rustc_lint::LateContext;
12 use rustc_middle::hir::map::Map;
13 use rustc_middle::hir::nested_filter;
14 use rustc_middle::ty::adjustment::Adjust;
15 use rustc_middle::ty::{self, Ty, TypeckResults};
16
17 /// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
18 /// bodies (i.e. closures) are visited.
19 /// If the callback returns `true`, the expr just provided to the callback is walked.
20 #[must_use]
21 pub fn expr_visitor<'tcx>(cx: &LateContext<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
22     struct V<'tcx, F> {
23         hir: Map<'tcx>,
24         f: F,
25     }
26     impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<'tcx, F> {
27         type NestedFilter = nested_filter::OnlyBodies;
28         fn nested_visit_map(&mut self) -> Self::Map {
29             self.hir
30         }
31
32         fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
33             if (self.f)(expr) {
34                 walk_expr(self, expr);
35             }
36         }
37     }
38     V { hir: cx.tcx.hir(), f }
39 }
40
41 /// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
42 /// bodies (i.e. closures) are not visited.
43 /// If the callback returns `true`, the expr just provided to the callback is walked.
44 #[must_use]
45 pub fn expr_visitor_no_bodies<'tcx>(f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
46     struct V<F>(F);
47     impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<F> {
48         fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
49             if (self.0)(e) {
50                 walk_expr(self, e);
51             }
52         }
53     }
54     V(f)
55 }
56
57 /// returns `true` if expr contains match expr desugared from try
58 fn contains_try(expr: &hir::Expr<'_>) -> bool {
59     let mut found = false;
60     expr_visitor_no_bodies(|e| {
61         if !found {
62             found = matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar));
63         }
64         !found
65     })
66     .visit_expr(expr);
67     found
68 }
69
70 pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool
71 where
72     F: FnMut(&'hir hir::Expr<'hir>) -> bool,
73 {
74     struct RetFinder<F> {
75         in_stmt: bool,
76         failed: bool,
77         cb: F,
78     }
79
80     struct WithStmtGuarg<'a, F> {
81         val: &'a mut RetFinder<F>,
82         prev_in_stmt: bool,
83     }
84
85     impl<F> RetFinder<F> {
86         fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
87             let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
88             WithStmtGuarg {
89                 val: self,
90                 prev_in_stmt,
91             }
92         }
93     }
94
95     impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
96         type Target = RetFinder<F>;
97
98         fn deref(&self) -> &Self::Target {
99             self.val
100         }
101     }
102
103     impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
104         fn deref_mut(&mut self) -> &mut Self::Target {
105             self.val
106         }
107     }
108
109     impl<F> Drop for WithStmtGuarg<'_, F> {
110         fn drop(&mut self) {
111             self.val.in_stmt = self.prev_in_stmt;
112         }
113     }
114
115     impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder<F> {
116         fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) {
117             intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt);
118         }
119
120         fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) {
121             if self.failed {
122                 return;
123             }
124             if self.in_stmt {
125                 match expr.kind {
126                     hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr),
127                     _ => intravisit::walk_expr(self, expr),
128                 }
129             } else {
130                 match expr.kind {
131                     hir::ExprKind::If(cond, then, else_opt) => {
132                         self.inside_stmt(true).visit_expr(cond);
133                         self.visit_expr(then);
134                         if let Some(el) = else_opt {
135                             self.visit_expr(el);
136                         }
137                     },
138                     hir::ExprKind::Match(cond, arms, _) => {
139                         self.inside_stmt(true).visit_expr(cond);
140                         for arm in arms {
141                             self.visit_expr(arm.body);
142                         }
143                     },
144                     hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr),
145                     hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr),
146                     _ => self.failed |= !(self.cb)(expr),
147                 }
148             }
149         }
150     }
151
152     !contains_try(expr) && {
153         let mut ret_finder = RetFinder {
154             in_stmt: false,
155             failed: false,
156             cb: callback,
157         };
158         ret_finder.visit_expr(expr);
159         !ret_finder.failed
160     }
161 }
162
163 /// A type which can be visited.
164 pub trait Visitable<'tcx> {
165     /// Calls the corresponding `visit_*` function on the visitor.
166     fn visit<V: Visitor<'tcx>>(self, visitor: &mut V);
167 }
168 macro_rules! visitable_ref {
169     ($t:ident, $f:ident) => {
170         impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> {
171             fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
172                 visitor.$f(self);
173             }
174         }
175     };
176 }
177 visitable_ref!(Arm, visit_arm);
178 visitable_ref!(Block, visit_block);
179 visitable_ref!(Body, visit_body);
180 visitable_ref!(Expr, visit_expr);
181 visitable_ref!(Stmt, visit_stmt);
182
183 // impl<'tcx, I: IntoIterator> Visitable<'tcx> for I
184 // where
185 //     I::Item: Visitable<'tcx>,
186 // {
187 //     fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
188 //         for x in self {
189 //             x.visit(visitor);
190 //         }
191 //     }
192 // }
193
194 /// Checks if the given resolved path is used in the given body.
195 pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
196     let mut found = false;
197     expr_visitor(cx, |e| {
198         if found {
199             return false;
200         }
201
202         if let ExprKind::Path(p) = &e.kind {
203             if cx.qpath_res(p, e.hir_id) == res {
204                 found = true;
205             }
206         }
207         !found
208     })
209     .visit_expr(&cx.tcx.hir().body(body).value);
210     found
211 }
212
213 /// Checks if the given local is used.
214 pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
215     let mut is_used = false;
216     let mut visitor = expr_visitor(cx, |expr| {
217         if !is_used {
218             is_used = path_to_local_id(expr, id);
219         }
220         !is_used
221     });
222     visitable.visit(&mut visitor);
223     drop(visitor);
224     is_used
225 }
226
227 /// Checks if the given expression is a constant.
228 pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
229     struct V<'a, 'tcx> {
230         cx: &'a LateContext<'tcx>,
231         is_const: bool,
232     }
233     impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
234         type NestedFilter = nested_filter::OnlyBodies;
235         fn nested_visit_map(&mut self) -> Self::Map {
236             self.cx.tcx.hir()
237         }
238
239         fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
240             if !self.is_const {
241                 return;
242             }
243             match e.kind {
244                 ExprKind::ConstBlock(_) => return,
245                 ExprKind::Call(
246                     &Expr {
247                         kind: ExprKind::Path(ref p),
248                         hir_id,
249                         ..
250                     },
251                     _,
252                 ) if self
253                     .cx
254                     .qpath_res(p, hir_id)
255                     .opt_def_id()
256                     .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
257                 ExprKind::MethodCall(..)
258                     if self
259                         .cx
260                         .typeck_results()
261                         .type_dependent_def_id(e.hir_id)
262                         .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
263                 ExprKind::Binary(_, lhs, rhs)
264                     if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty()
265                         && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {},
266                 ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_ref() => (),
267                 ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (),
268                 ExprKind::Index(base, _)
269                     if matches!(
270                         self.cx.typeck_results().expr_ty(base).peel_refs().kind(),
271                         ty::Slice(_) | ty::Array(..)
272                     ) => {},
273                 ExprKind::Path(ref p)
274                     if matches!(
275                         self.cx.qpath_res(p, e.hir_id),
276                         Res::Def(
277                             DefKind::Const
278                                 | DefKind::AssocConst
279                                 | DefKind::AnonConst
280                                 | DefKind::ConstParam
281                                 | DefKind::Ctor(..)
282                                 | DefKind::Fn
283                                 | DefKind::AssocFn,
284                             _
285                         ) | Res::SelfCtor(_)
286                     ) => {},
287
288                 ExprKind::AddrOf(..)
289                 | ExprKind::Array(_)
290                 | ExprKind::Block(..)
291                 | ExprKind::Cast(..)
292                 | ExprKind::DropTemps(_)
293                 | ExprKind::Field(..)
294                 | ExprKind::If(..)
295                 | ExprKind::Let(..)
296                 | ExprKind::Lit(_)
297                 | ExprKind::Match(..)
298                 | ExprKind::Repeat(..)
299                 | ExprKind::Struct(..)
300                 | ExprKind::Tup(_)
301                 | ExprKind::Type(..) => (),
302
303                 _ => {
304                     self.is_const = false;
305                     return;
306                 },
307             }
308             walk_expr(self, e);
309         }
310     }
311
312     let mut v = V { cx, is_const: true };
313     v.visit_expr(e);
314     v.is_const
315 }
316
317 /// Checks if the given expression performs an unsafe operation outside of an unsafe block.
318 pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
319     struct V<'a, 'tcx> {
320         cx: &'a LateContext<'tcx>,
321         is_unsafe: bool,
322     }
323     impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
324         type NestedFilter = nested_filter::OnlyBodies;
325         fn nested_visit_map(&mut self) -> Self::Map {
326             self.cx.tcx.hir()
327         }
328         fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
329             if self.is_unsafe {
330                 return;
331             }
332             match e.kind {
333                 ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => {
334                     self.is_unsafe = true;
335                 },
336                 ExprKind::MethodCall(..)
337                     if self
338                         .cx
339                         .typeck_results()
340                         .type_dependent_def_id(e.hir_id)
341                         .map_or(false, |id| self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe) =>
342                 {
343                     self.is_unsafe = true;
344                 },
345                 ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
346                     ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
347                     ty::FnPtr(sig) if sig.unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
348                     _ => walk_expr(self, e),
349                 },
350                 ExprKind::Path(ref p)
351                     if self
352                         .cx
353                         .qpath_res(p, e.hir_id)
354                         .opt_def_id()
355                         .map_or(false, |id| self.cx.tcx.is_mutable_static(id)) =>
356                 {
357                     self.is_unsafe = true;
358                 },
359                 _ => walk_expr(self, e),
360             }
361         }
362         fn visit_block(&mut self, b: &'tcx Block<'_>) {
363             if !matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
364                 walk_block(self, b);
365             }
366         }
367         fn visit_nested_item(&mut self, id: ItemId) {
368             if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind {
369                 self.is_unsafe = i.unsafety == Unsafety::Unsafe;
370             }
371         }
372     }
373     let mut v = V { cx, is_unsafe: false };
374     v.visit_expr(e);
375     v.is_unsafe
376 }
377
378 /// Checks if the given expression contains an unsafe block
379 pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
380     struct V<'cx, 'tcx> {
381         cx: &'cx LateContext<'tcx>,
382         found_unsafe: bool,
383     }
384     impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
385         type NestedFilter = nested_filter::OnlyBodies;
386         fn nested_visit_map(&mut self) -> Self::Map {
387             self.cx.tcx.hir()
388         }
389
390         fn visit_block(&mut self, b: &'tcx Block<'_>) {
391             if self.found_unsafe {
392                 return;
393             }
394             if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
395                 self.found_unsafe = true;
396                 return;
397             }
398             walk_block(self, b);
399         }
400     }
401     let mut v = V {
402         cx,
403         found_unsafe: false,
404     };
405     v.visit_expr(e);
406     v.found_unsafe
407 }
408
409 /// Runs the given function for each sub-expression producing the final value consumed by the parent
410 /// of the give expression.
411 ///
412 /// e.g. for the following expression
413 /// ```rust,ignore
414 /// if foo {
415 ///     f(0)
416 /// } else {
417 ///     1 + 1
418 /// }
419 /// ```
420 /// this will pass both `f(0)` and `1+1` to the given function.
421 pub fn for_each_value_source<'tcx, B>(
422     e: &'tcx Expr<'tcx>,
423     f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
424 ) -> ControlFlow<B> {
425     match e.kind {
426         ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f),
427         ExprKind::Match(_, arms, _) => {
428             for arm in arms {
429                 for_each_value_source(arm.body, f)?;
430             }
431             ControlFlow::Continue(())
432         },
433         ExprKind::If(_, if_expr, Some(else_expr)) => {
434             for_each_value_source(if_expr, f)?;
435             for_each_value_source(else_expr, f)
436         },
437         ExprKind::DropTemps(e) => for_each_value_source(e, f),
438         _ => f(e),
439     }
440 }
441
442 /// Runs the given function for each path expression referencing the given local which occur after
443 /// the given expression.
444 pub fn for_each_local_use_after_expr<'tcx, B>(
445     cx: &LateContext<'tcx>,
446     local_id: HirId,
447     expr_id: HirId,
448     f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
449 ) -> ControlFlow<B> {
450     struct V<'cx, 'tcx, F, B> {
451         cx: &'cx LateContext<'tcx>,
452         local_id: HirId,
453         expr_id: HirId,
454         found: bool,
455         res: ControlFlow<B>,
456         f: F,
457     }
458     impl<'cx, 'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'cx, 'tcx, F, B> {
459         type NestedFilter = nested_filter::OnlyBodies;
460         fn nested_visit_map(&mut self) -> Self::Map {
461             self.cx.tcx.hir()
462         }
463
464         fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
465             if !self.found {
466                 if e.hir_id == self.expr_id {
467                     self.found = true;
468                 } else {
469                     walk_expr(self, e);
470                 }
471                 return;
472             }
473             if self.res.is_break() {
474                 return;
475             }
476             if path_to_local_id(e, self.local_id) {
477                 self.res = (self.f)(e);
478             } else {
479                 walk_expr(self, e);
480             }
481         }
482     }
483
484     if let Some(b) = get_enclosing_block(cx, local_id) {
485         let mut v = V {
486             cx,
487             local_id,
488             expr_id,
489             found: false,
490             res: ControlFlow::Continue(()),
491             f,
492         };
493         v.visit_block(b);
494         v.res
495     } else {
496         ControlFlow::Continue(())
497     }
498 }
499
500 // Calls the given function for every unconsumed temporary created by the expression. Note the
501 // function is only guaranteed to be called for types which need to be dropped, but it may be called
502 // for other types.
503 pub fn for_each_unconsumed_temporary<'tcx, B>(
504     cx: &LateContext<'tcx>,
505     e: &'tcx Expr<'tcx>,
506     mut f: impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
507 ) -> ControlFlow<B> {
508     // Todo: Handle partially consumed values.
509     fn helper<'tcx, B>(
510         typeck: &'tcx TypeckResults<'tcx>,
511         consume: bool,
512         e: &'tcx Expr<'tcx>,
513         f: &mut impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
514     ) -> ControlFlow<B> {
515         if !consume
516             || matches!(
517                 typeck.expr_adjustments(e),
518                 [adjust, ..] if matches!(adjust.kind, Adjust::Borrow(_) | Adjust::Deref(_))
519             )
520         {
521             match e.kind {
522                 ExprKind::Path(QPath::Resolved(None, p))
523                     if matches!(p.res, Res::Def(DefKind::Ctor(_, CtorKind::Const), _)) =>
524                 {
525                     f(typeck.expr_ty(e))?;
526                 },
527                 ExprKind::Path(_)
528                 | ExprKind::Unary(UnOp::Deref, _)
529                 | ExprKind::Index(..)
530                 | ExprKind::Field(..)
531                 | ExprKind::AddrOf(..) => (),
532                 _ => f(typeck.expr_ty(e))?,
533             }
534         }
535         match e.kind {
536             ExprKind::AddrOf(_, _, e)
537             | ExprKind::Field(e, _)
538             | ExprKind::Unary(UnOp::Deref, e)
539             | ExprKind::Match(e, ..)
540             | ExprKind::Let(&Let { init: e, .. }) => {
541                 helper(typeck, false, e, f)?;
542             },
543             ExprKind::Block(&Block { expr: Some(e), .. }, _)
544             | ExprKind::Box(e)
545             | ExprKind::Cast(e, _)
546             | ExprKind::Unary(_, e) => {
547                 helper(typeck, true, e, f)?;
548             },
549             ExprKind::Call(callee, args) => {
550                 helper(typeck, true, callee, f)?;
551                 for arg in args {
552                     helper(typeck, true, arg, f)?;
553                 }
554             },
555             ExprKind::MethodCall(_, args, _) | ExprKind::Tup(args) | ExprKind::Array(args) => {
556                 for arg in args {
557                     helper(typeck, true, arg, f)?;
558                 }
559             },
560             ExprKind::Index(borrowed, consumed)
561             | ExprKind::Assign(borrowed, consumed, _)
562             | ExprKind::AssignOp(_, borrowed, consumed) => {
563                 helper(typeck, false, borrowed, f)?;
564                 helper(typeck, true, consumed, f)?;
565             },
566             ExprKind::Binary(_, lhs, rhs) => {
567                 helper(typeck, true, lhs, f)?;
568                 helper(typeck, true, rhs, f)?;
569             },
570             ExprKind::Struct(_, fields, default) => {
571                 for field in fields {
572                     helper(typeck, true, field.expr, f)?;
573                 }
574                 if let Some(default) = default {
575                     helper(typeck, false, default, f)?;
576                 }
577             },
578             ExprKind::If(cond, then, else_expr) => {
579                 helper(typeck, true, cond, f)?;
580                 helper(typeck, true, then, f)?;
581                 if let Some(else_expr) = else_expr {
582                     helper(typeck, true, else_expr, f)?;
583                 }
584             },
585             ExprKind::Type(e, _) => {
586                 helper(typeck, consume, e, f)?;
587             },
588
589             // Either drops temporaries, jumps out of the current expression, or has no sub expression.
590             ExprKind::DropTemps(_)
591             | ExprKind::Ret(_)
592             | ExprKind::Break(..)
593             | ExprKind::Yield(..)
594             | ExprKind::Block(..)
595             | ExprKind::Loop(..)
596             | ExprKind::Repeat(..)
597             | ExprKind::Lit(_)
598             | ExprKind::ConstBlock(_)
599             | ExprKind::Closure { .. }
600             | ExprKind::Path(_)
601             | ExprKind::Continue(_)
602             | ExprKind::InlineAsm(_)
603             | ExprKind::Err => (),
604         }
605         ControlFlow::Continue(())
606     }
607     helper(cx.typeck_results(), true, e, &mut f)
608 }
609
610 pub fn any_temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
611     for_each_unconsumed_temporary(cx, e, |ty| {
612         if needs_ordered_drop(cx, ty) {
613             ControlFlow::Break(())
614         } else {
615             ControlFlow::Continue(())
616         }
617     })
618     .is_break()
619 }