1 use crate::path_to_local_id;
3 use rustc_hir::def::{DefKind, Res};
4 use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
6 Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, UnsafeSource,
9 use rustc_lint::LateContext;
10 use rustc_middle::hir::map::Map;
11 use rustc_middle::hir::nested_filter;
14 /// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
15 /// bodies (i.e. closures) are visited.
16 /// If the callback returns `true`, the expr just provided to the callback is walked.
18 pub fn expr_visitor<'tcx>(cx: &LateContext<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
23 impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<'tcx, F> {
24 type NestedFilter = nested_filter::OnlyBodies;
25 fn nested_visit_map(&mut self) -> Self::Map {
29 fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
31 walk_expr(self, expr);
35 V { hir: cx.tcx.hir(), f }
38 /// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
39 /// bodies (i.e. closures) are not visited.
40 /// If the callback returns `true`, the expr just provided to the callback is walked.
42 pub fn expr_visitor_no_bodies<'tcx>(f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
44 impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<F> {
45 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
54 /// returns `true` if expr contains match expr desugared from try
55 fn contains_try(expr: &hir::Expr<'_>) -> bool {
56 let mut found = false;
57 expr_visitor_no_bodies(|e| {
59 found = matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar));
67 pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool
69 F: FnMut(&'hir hir::Expr<'hir>) -> bool,
77 struct WithStmtGuarg<'a, F> {
78 val: &'a mut RetFinder<F>,
82 impl<F> RetFinder<F> {
83 fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
84 let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
92 impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
93 type Target = RetFinder<F>;
95 fn deref(&self) -> &Self::Target {
100 impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
101 fn deref_mut(&mut self) -> &mut Self::Target {
106 impl<F> Drop for WithStmtGuarg<'_, F> {
108 self.val.in_stmt = self.prev_in_stmt;
112 impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder<F> {
113 fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) {
114 intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt);
117 fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) {
123 hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr),
124 _ => intravisit::walk_expr(self, expr),
128 hir::ExprKind::If(cond, then, else_opt) => {
129 self.inside_stmt(true).visit_expr(cond);
130 self.visit_expr(then);
131 if let Some(el) = else_opt {
135 hir::ExprKind::Match(cond, arms, _) => {
136 self.inside_stmt(true).visit_expr(cond);
138 self.visit_expr(arm.body);
141 hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr),
142 hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr),
143 _ => self.failed |= !(self.cb)(expr),
149 !contains_try(expr) && {
150 let mut ret_finder = RetFinder {
155 ret_finder.visit_expr(expr);
160 /// A type which can be visited.
161 pub trait Visitable<'tcx> {
162 /// Calls the corresponding `visit_*` function on the visitor.
163 fn visit<V: Visitor<'tcx>>(self, visitor: &mut V);
165 macro_rules! visitable_ref {
166 ($t:ident, $f:ident) => {
167 impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> {
168 fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
174 visitable_ref!(Arm, visit_arm);
175 visitable_ref!(Block, visit_block);
176 visitable_ref!(Body, visit_body);
177 visitable_ref!(Expr, visit_expr);
178 visitable_ref!(Stmt, visit_stmt);
180 // impl<'tcx, I: IntoIterator> Visitable<'tcx> for I
182 // I::Item: Visitable<'tcx>,
184 // fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
191 /// Checks if the given resolved path is used in the given body.
192 pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
193 let mut found = false;
194 expr_visitor(cx, |e| {
199 if let ExprKind::Path(p) = &e.kind {
200 if cx.qpath_res(p, e.hir_id) == res {
206 .visit_expr(&cx.tcx.hir().body(body).value);
210 /// Checks if the given local is used.
211 pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
212 let mut is_used = false;
213 let mut visitor = expr_visitor(cx, |expr| {
215 is_used = path_to_local_id(expr, id);
219 visitable.visit(&mut visitor);
224 /// Checks if the given expression is a constant.
225 pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
227 cx: &'a LateContext<'tcx>,
230 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
231 type NestedFilter = nested_filter::OnlyBodies;
232 fn nested_visit_map(&mut self) -> Self::Map {
236 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
241 ExprKind::ConstBlock(_) => return,
244 kind: ExprKind::Path(ref p),
251 .qpath_res(p, hir_id)
253 .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
254 ExprKind::MethodCall(..)
258 .type_dependent_def_id(e.hir_id)
259 .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
260 ExprKind::Binary(_, lhs, rhs)
261 if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty()
262 && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {},
263 ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_ref() => (),
264 ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (),
265 ExprKind::Index(base, _)
267 self.cx.typeck_results().expr_ty(base).peel_refs().kind(),
268 ty::Slice(_) | ty::Array(..)
270 ExprKind::Path(ref p)
272 self.cx.qpath_res(p, e.hir_id),
275 | DefKind::AssocConst
277 | DefKind::ConstParam
287 | ExprKind::Block(..)
289 | ExprKind::DropTemps(_)
290 | ExprKind::Field(..)
294 | ExprKind::Match(..)
295 | ExprKind::Repeat(..)
296 | ExprKind::Struct(..)
298 | ExprKind::Type(..) => (),
301 self.is_const = false;
309 let mut v = V { cx, is_const: true };
314 /// Checks if the given expression performs an unsafe operation outside of an unsafe block.
315 pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
317 cx: &'a LateContext<'tcx>,
320 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
321 type NestedFilter = nested_filter::OnlyBodies;
322 fn nested_visit_map(&mut self) -> Self::Map {
325 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
330 ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => {
331 self.is_unsafe = true;
333 ExprKind::MethodCall(..)
337 .type_dependent_def_id(e.hir_id)
338 .map_or(false, |id| self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe) =>
340 self.is_unsafe = true;
342 ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
343 ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
344 ty::FnPtr(sig) if sig.unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
345 _ => walk_expr(self, e),
347 ExprKind::Path(ref p)
350 .qpath_res(p, e.hir_id)
352 .map_or(false, |id| self.cx.tcx.is_mutable_static(id)) =>
354 self.is_unsafe = true;
356 _ => walk_expr(self, e),
359 fn visit_block(&mut self, b: &'tcx Block<'_>) {
360 if !matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
364 fn visit_nested_item(&mut self, id: ItemId) {
365 if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind {
366 self.is_unsafe = i.unsafety == Unsafety::Unsafe;
370 let mut v = V { cx, is_unsafe: false };
375 /// Checks if the given expression contains an unsafe block
376 pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
377 struct V<'cx, 'tcx> {
378 cx: &'cx LateContext<'tcx>,
381 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
382 type NestedFilter = nested_filter::OnlyBodies;
383 fn nested_visit_map(&mut self) -> Self::Map {
387 fn visit_block(&mut self, b: &'tcx Block<'_>) {
388 if self.found_unsafe {
391 if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
392 self.found_unsafe = true;