use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::intravisit::{self, walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{Arm, Expr, ExprKind, HirId, QPath, Stmt}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; /// returns `true` if expr contains match expr desugared from try fn contains_try(expr: &hir::Expr<'_>) -> bool { struct TryFinder { found: bool, } impl<'hir> intravisit::Visitor<'hir> for TryFinder { type Map = Map<'hir>; fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { intravisit::NestedVisitorMap::None } fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { if self.found { return; } match expr.kind { hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar) => self.found = true, _ => intravisit::walk_expr(self, expr), } } } let mut visitor = TryFinder { found: false }; visitor.visit_expr(expr); visitor.found } pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool where F: FnMut(&'hir hir::Expr<'hir>) -> bool, { struct RetFinder { in_stmt: bool, failed: bool, cb: F, } struct WithStmtGuarg<'a, F> { val: &'a mut RetFinder, prev_in_stmt: bool, } impl RetFinder { fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> { let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt); WithStmtGuarg { val: self, prev_in_stmt, } } } impl std::ops::Deref for WithStmtGuarg<'_, F> { type Target = RetFinder; fn deref(&self) -> &Self::Target { self.val } } impl std::ops::DerefMut for WithStmtGuarg<'_, F> { fn deref_mut(&mut self) -> &mut Self::Target { self.val } } impl Drop for WithStmtGuarg<'_, F> { fn drop(&mut self) { self.val.in_stmt = self.prev_in_stmt; } } impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder { type Map = Map<'hir>; fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { intravisit::NestedVisitorMap::None } fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) { intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt) } fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) { if self.failed { return; } if self.in_stmt { match expr.kind { hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr), _ => intravisit::walk_expr(self, expr), } } else { match expr.kind { hir::ExprKind::Match(cond, arms, _) => { self.inside_stmt(true).visit_expr(cond); for arm in arms { self.visit_expr(arm.body); } }, hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr), hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr), _ => self.failed |= !(self.cb)(expr), } } } } !contains_try(expr) && { let mut ret_finder = RetFinder { in_stmt: false, failed: false, cb: callback, }; ret_finder.visit_expr(expr); !ret_finder.failed } } pub struct LocalUsedVisitor { pub local_hir_id: HirId, pub used: bool, } impl LocalUsedVisitor { pub fn new(local_hir_id: HirId) -> Self { Self { local_hir_id, used: false, } } fn check(&mut self, t: T, visit: fn(&mut Self, T)) -> bool { visit(self, t); std::mem::replace(&mut self.used, false) } pub fn check_arm(&mut self, arm: &Arm<'_>) -> bool { self.check(arm, Self::visit_arm) } pub fn check_expr(&mut self, expr: &Expr<'_>) -> bool { self.check(expr, Self::visit_expr) } pub fn check_stmt(&mut self, stmt: &Stmt<'_>) -> bool { self.check(stmt, Self::visit_stmt) } } impl<'v> Visitor<'v> for LocalUsedVisitor { type Map = Map<'v>; fn visit_expr(&mut self, expr: &'v Expr<'v>) { if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind { if let Res::Local(id) = path.res { if id == self.local_hir_id { self.used = true; return; } } } walk_expr(self, expr); } fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } }