Skip to content

Commit d251bd9

Browse files
committed
Add for_each_expr
1 parent 54feac1 commit d251bd9

File tree

1 file changed

+69
-1
lines changed

1 file changed

+69
-1
lines changed

clippy_utils/src/visitors.rs

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,82 @@ use rustc_hir as hir;
55
use rustc_hir::def::{CtorKind, DefKind, Res};
66
use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
77
use rustc_hir::{
8-
Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, QPath, Stmt, UnOp,
8+
Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, Pat, QPath, Stmt, UnOp,
99
UnsafeSource, Unsafety,
1010
};
1111
use rustc_lint::LateContext;
1212
use rustc_middle::hir::map::Map;
1313
use rustc_middle::hir::nested_filter;
1414
use rustc_middle::ty::adjustment::Adjust;
1515
use rustc_middle::ty::{self, Ty, TypeckResults};
16+
use rustc_span::Span;
17+
18+
mod internal {
19+
/// Trait for visitor functions to control whether or not to descend to child nodes. Implemented
20+
/// for only two types. `()` always descends. `Descend` allows controlled descent.
21+
pub trait Continue {
22+
fn descend(&self) -> bool;
23+
}
24+
}
25+
use internal::Continue;
26+
27+
impl Continue for () {
28+
fn descend(&self) -> bool {
29+
true
30+
}
31+
}
32+
33+
/// Allows for controlled descent whe using visitor functions. Use `()` instead when always
34+
/// descending into child nodes.
35+
#[derive(Clone, Copy)]
36+
pub enum Descend {
37+
Yes,
38+
No,
39+
}
40+
impl From<bool> for Descend {
41+
fn from(from: bool) -> Self {
42+
if from { Self::Yes } else { Self::No }
43+
}
44+
}
45+
impl Continue for Descend {
46+
fn descend(&self) -> bool {
47+
matches!(self, Self::Yes)
48+
}
49+
}
50+
51+
/// Calls the given function once for each expression contained. This does not enter any bodies or
52+
/// nested items.
53+
pub fn for_each_expr<'tcx, B, C: Continue>(
54+
node: impl Visitable<'tcx>,
55+
f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
56+
) -> Option<B> {
57+
struct V<B, F> {
58+
f: F,
59+
res: Option<B>,
60+
}
61+
impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<B, F> {
62+
fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
63+
if self.res.is_some() {
64+
return;
65+
}
66+
match (self.f)(e) {
67+
ControlFlow::Continue(c) if c.descend() => walk_expr(self, e),
68+
ControlFlow::Break(b) => self.res = Some(b),
69+
ControlFlow::Continue(_) => (),
70+
}
71+
}
72+
73+
// Avoid unnecessary `walk_*` calls.
74+
fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) {}
75+
fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {}
76+
fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {}
77+
// Avoid monomorphising all `visit_*` functions.
78+
fn visit_nested_item(&mut self, _: ItemId) {}
79+
}
80+
let mut v = V { f, res: None };
81+
node.visit(&mut v);
82+
v.res
83+
}
1684

1785
/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
1886
/// bodies (i.e. closures) are visited.

0 commit comments

Comments
 (0)