]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | use crate::path_to_local_id; |
2 | use rustc_hir as hir; | |
3 | use rustc_hir::intravisit::{self, walk_expr, NestedVisitorMap, Visitor}; | |
4 | use rustc_hir::{Arm, Body, Expr, 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 | } |