]> git.lizzy.rs Git - rust.git/blob - clippy_utils/src/visitors.rs
Improve `implicit_return`
[rust.git] / clippy_utils / src / visitors.rs
1 use crate::path_to_local_id;
2 use rustc_hir as hir;
3 use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor};
4 use rustc_hir::{Arm, Block, Body, Destination, Expr, ExprKind, HirId, Stmt};
5 use rustc_lint::LateContext;
6 use rustc_middle::hir::map::Map;
7
8 /// returns `true` if expr contains match expr desugared from try
9 fn contains_try(expr: &hir::Expr<'_>) -> bool {
10     struct TryFinder {
11         found: bool,
12     }
13
14     impl<'hir> intravisit::Visitor<'hir> for TryFinder {
15         type Map = Map<'hir>;
16
17         fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
18             intravisit::NestedVisitorMap::None
19         }
20
21         fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
22             if self.found {
23                 return;
24             }
25             match expr.kind {
26                 hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar) => self.found = true,
27                 _ => intravisit::walk_expr(self, expr),
28             }
29         }
30     }
31
32     let mut visitor = TryFinder { found: false };
33     visitor.visit_expr(expr);
34     visitor.found
35 }
36
37 pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool
38 where
39     F: FnMut(&'hir hir::Expr<'hir>) -> bool,
40 {
41     struct RetFinder<F> {
42         in_stmt: bool,
43         failed: bool,
44         cb: F,
45     }
46
47     struct WithStmtGuarg<'a, F> {
48         val: &'a mut RetFinder<F>,
49         prev_in_stmt: bool,
50     }
51
52     impl<F> RetFinder<F> {
53         fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
54             let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
55             WithStmtGuarg {
56                 val: self,
57                 prev_in_stmt,
58             }
59         }
60     }
61
62     impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
63         type Target = RetFinder<F>;
64
65         fn deref(&self) -> &Self::Target {
66             self.val
67         }
68     }
69
70     impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
71         fn deref_mut(&mut self) -> &mut Self::Target {
72             self.val
73         }
74     }
75
76     impl<F> Drop for WithStmtGuarg<'_, F> {
77         fn drop(&mut self) {
78             self.val.in_stmt = self.prev_in_stmt;
79         }
80     }
81
82     impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder<F> {
83         type Map = Map<'hir>;
84
85         fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
86             intravisit::NestedVisitorMap::None
87         }
88
89         fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) {
90             intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt)
91         }
92
93         fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) {
94             if self.failed {
95                 return;
96             }
97             if self.in_stmt {
98                 match expr.kind {
99                     hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr),
100                     _ => intravisit::walk_expr(self, expr),
101                 }
102             } else {
103                 match expr.kind {
104                     hir::ExprKind::If(cond, then, else_opt) => {
105                         self.inside_stmt(true).visit_expr(cond);
106                         self.visit_expr(then);
107                         if let Some(el) = else_opt {
108                             self.visit_expr(el);
109                         }
110                     },
111                     hir::ExprKind::Match(cond, arms, _) => {
112                         self.inside_stmt(true).visit_expr(cond);
113                         for arm in arms {
114                             self.visit_expr(arm.body);
115                         }
116                     },
117                     hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr),
118                     hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr),
119                     _ => self.failed |= !(self.cb)(expr),
120                 }
121             }
122         }
123     }
124
125     !contains_try(expr) && {
126         let mut ret_finder = RetFinder {
127             in_stmt: false,
128             failed: false,
129             cb: callback,
130         };
131         ret_finder.visit_expr(expr);
132         !ret_finder.failed
133     }
134 }
135
136 pub struct LocalUsedVisitor<'hir> {
137     hir: Map<'hir>,
138     pub local_hir_id: HirId,
139     pub used: bool,
140 }
141
142 impl<'hir> LocalUsedVisitor<'hir> {
143     pub fn new(cx: &LateContext<'hir>, local_hir_id: HirId) -> Self {
144         Self {
145             hir: cx.tcx.hir(),
146             local_hir_id,
147             used: false,
148         }
149     }
150
151     fn check<T>(&mut self, t: T, visit: fn(&mut Self, T)) -> bool {
152         visit(self, t);
153         std::mem::replace(&mut self.used, false)
154     }
155
156     pub fn check_arm(&mut self, arm: &'hir Arm<'_>) -> bool {
157         self.check(arm, Self::visit_arm)
158     }
159
160     pub fn check_body(&mut self, body: &'hir Body<'_>) -> bool {
161         self.check(body, Self::visit_body)
162     }
163
164     pub fn check_expr(&mut self, expr: &'hir Expr<'_>) -> bool {
165         self.check(expr, Self::visit_expr)
166     }
167
168     pub fn check_stmt(&mut self, stmt: &'hir Stmt<'_>) -> bool {
169         self.check(stmt, Self::visit_stmt)
170     }
171 }
172
173 impl<'v> Visitor<'v> for LocalUsedVisitor<'v> {
174     type Map = Map<'v>;
175
176     fn visit_expr(&mut self, expr: &'v Expr<'v>) {
177         if self.used {
178             return;
179         }
180         if path_to_local_id(expr, self.local_hir_id) {
181             self.used = true;
182         } else {
183             walk_expr(self, expr);
184         }
185     }
186
187     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
188         NestedVisitorMap::OnlyBodies(self.hir)
189     }
190 }
191
192 pub trait Visitable<'tcx> {
193     fn visit<V: Visitor<'tcx>>(self, v: &mut V);
194 }
195 impl Visitable<'tcx> for &'tcx Expr<'tcx> {
196     fn visit<V: Visitor<'tcx>>(self, v: &mut V) {
197         v.visit_expr(self)
198     }
199 }
200 impl Visitable<'tcx> for &'tcx Block<'tcx> {
201     fn visit<V: Visitor<'tcx>>(self, v: &mut V) {
202         v.visit_block(self)
203     }
204 }
205 impl<'tcx> Visitable<'tcx> for &'tcx Stmt<'tcx> {
206     fn visit<V: Visitor<'tcx>>(self, v: &mut V) {
207         v.visit_stmt(self)
208     }
209 }
210 impl<'tcx> Visitable<'tcx> for &'tcx Body<'tcx> {
211     fn visit<V: Visitor<'tcx>>(self, v: &mut V) {
212         v.visit_body(self)
213     }
214 }
215 impl<'tcx> Visitable<'tcx> for &'tcx Arm<'tcx> {
216     fn visit<V: Visitor<'tcx>>(self, v: &mut V) {
217         v.visit_arm(self)
218     }
219 }
220
221 pub fn visit_break_exprs<'tcx>(
222     node: impl Visitable<'tcx>,
223     f: impl FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>),
224 ) {
225     struct V<F>(F);
226     impl<'tcx, F: FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>)> Visitor<'tcx> for V<F> {
227         type Map = ErasedMap<'tcx>;
228         fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
229             NestedVisitorMap::None
230         }
231
232         fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
233             if let ExprKind::Break(dest, sub_expr) = e.kind {
234                 self.0(e, dest, sub_expr)
235             }
236             walk_expr(self, e);
237         }
238     }
239
240     node.visit(&mut V(f));
241 }