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, Unsafety,
8 use rustc_lint::LateContext;
9 use rustc_middle::hir::map::Map;
10 use rustc_middle::hir::nested_filter;
13 /// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
14 /// bodies (i.e. closures) are visited.
15 /// If the callback returns `true`, the expr just provided to the callback is walked.
17 pub fn expr_visitor<'tcx>(cx: &LateContext<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
22 impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<'tcx, F> {
23 type NestedFilter = nested_filter::OnlyBodies;
24 fn nested_visit_map(&mut self) -> Self::Map {
28 fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
30 walk_expr(self, expr);
34 V { hir: cx.tcx.hir(), f }
37 /// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
38 /// bodies (i.e. closures) are not visited.
39 /// If the callback returns `true`, the expr just provided to the callback is walked.
41 pub fn expr_visitor_no_bodies<'tcx>(f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
43 impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<F> {
44 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
53 /// returns `true` if expr contains match expr desugared from try
54 fn contains_try(expr: &hir::Expr<'_>) -> bool {
55 let mut found = false;
56 expr_visitor_no_bodies(|e| {
58 found = matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar));
66 pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool
68 F: FnMut(&'hir hir::Expr<'hir>) -> bool,
76 struct WithStmtGuarg<'a, F> {
77 val: &'a mut RetFinder<F>,
81 impl<F> RetFinder<F> {
82 fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
83 let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
91 impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
92 type Target = RetFinder<F>;
94 fn deref(&self) -> &Self::Target {
99 impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
100 fn deref_mut(&mut self) -> &mut Self::Target {
105 impl<F> Drop for WithStmtGuarg<'_, F> {
107 self.val.in_stmt = self.prev_in_stmt;
111 impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder<F> {
112 fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) {
113 intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt);
116 fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) {
122 hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr),
123 _ => intravisit::walk_expr(self, expr),
127 hir::ExprKind::If(cond, then, else_opt) => {
128 self.inside_stmt(true).visit_expr(cond);
129 self.visit_expr(then);
130 if let Some(el) = else_opt {
134 hir::ExprKind::Match(cond, arms, _) => {
135 self.inside_stmt(true).visit_expr(cond);
137 self.visit_expr(arm.body);
140 hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr),
141 hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr),
142 _ => self.failed |= !(self.cb)(expr),
148 !contains_try(expr) && {
149 let mut ret_finder = RetFinder {
154 ret_finder.visit_expr(expr);
159 /// A type which can be visited.
160 pub trait Visitable<'tcx> {
161 /// Calls the corresponding `visit_*` function on the visitor.
162 fn visit<V: Visitor<'tcx>>(self, visitor: &mut V);
164 macro_rules! visitable_ref {
165 ($t:ident, $f:ident) => {
166 impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> {
167 fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
173 visitable_ref!(Arm, visit_arm);
174 visitable_ref!(Block, visit_block);
175 visitable_ref!(Body, visit_body);
176 visitable_ref!(Expr, visit_expr);
177 visitable_ref!(Stmt, visit_stmt);
179 // impl<'tcx, I: IntoIterator> Visitable<'tcx> for I
181 // I::Item: Visitable<'tcx>,
183 // fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
190 /// Checks if the given resolved path is used in the given body.
191 pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
192 let mut found = false;
193 expr_visitor(cx, |e| {
198 if let ExprKind::Path(p) = &e.kind {
199 if cx.qpath_res(p, e.hir_id) == res {
205 .visit_expr(&cx.tcx.hir().body(body).value);
209 /// Checks if the given local is used.
210 pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
211 let mut is_used = false;
212 let mut visitor = expr_visitor(cx, |expr| {
214 is_used = path_to_local_id(expr, id);
218 visitable.visit(&mut visitor);
223 /// Checks if the given expression is a constant.
224 pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
226 cx: &'a LateContext<'tcx>,
229 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
230 type NestedFilter = nested_filter::OnlyBodies;
231 fn nested_visit_map(&mut self) -> Self::Map {
235 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
240 ExprKind::ConstBlock(_) => return,
243 kind: ExprKind::Path(ref p),
250 .qpath_res(p, hir_id)
252 .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
253 ExprKind::MethodCall(..)
257 .type_dependent_def_id(e.hir_id)
258 .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
259 ExprKind::Binary(_, lhs, rhs)
260 if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty()
261 && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {},
262 ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_ref() => (),
263 ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (),
264 ExprKind::Index(base, _)
266 self.cx.typeck_results().expr_ty(base).peel_refs().kind(),
267 ty::Slice(_) | ty::Array(..)
269 ExprKind::Path(ref p)
271 self.cx.qpath_res(p, e.hir_id),
274 | DefKind::AssocConst
276 | DefKind::ConstParam
286 | ExprKind::Block(..)
288 | ExprKind::DropTemps(_)
289 | ExprKind::Field(..)
293 | ExprKind::Match(..)
294 | ExprKind::Repeat(..)
295 | ExprKind::Struct(..)
297 | ExprKind::Type(..) => (),
300 self.is_const = false;
308 let mut v = V { cx, is_const: true };
313 /// Checks if the given expression performs an unsafe operation outside of an unsafe block.
314 pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
316 cx: &'a LateContext<'tcx>,
319 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
320 type NestedFilter = nested_filter::OnlyBodies;
321 fn nested_visit_map(&mut self) -> Self::Map {
324 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
329 ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => {
330 self.is_unsafe = true;
332 ExprKind::MethodCall(..)
336 .type_dependent_def_id(e.hir_id)
337 .map_or(false, |id| self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe) =>
339 self.is_unsafe = true;
341 ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
342 ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
343 ty::FnPtr(sig) if sig.unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
344 _ => walk_expr(self, e),
346 ExprKind::Path(ref p)
349 .qpath_res(p, e.hir_id)
351 .map_or(false, |id| self.cx.tcx.is_mutable_static(id)) =>
353 self.is_unsafe = true;
355 _ => walk_expr(self, e),
358 fn visit_block(&mut self, b: &'tcx Block<'_>) {
359 if !matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
363 fn visit_nested_item(&mut self, id: ItemId) {
364 if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind {
365 self.is_unsafe = i.unsafety == Unsafety::Unsafe;
369 let mut v = V { cx, is_unsafe: false };