+/// Walks the HIR tree from the given expression, up to the node where the value produced by the
+/// expression is consumed. Calls the function for every node encountered this way until it returns
+/// `Some`.
+///
+/// This allows walking through `if`, `match`, `break`, block expressions to find where the value
+/// produced by the expression is consumed.
+pub fn walk_to_expr_usage<'tcx, T>(
+ cx: &LateContext<'tcx>,
+ e: &Expr<'tcx>,
+ mut f: impl FnMut(Node<'tcx>, HirId) -> Option<T>,
+) -> Option<T> {
+ let map = cx.tcx.hir();
+ let mut iter = map.parent_iter(e.hir_id);
+ let mut child_id = e.hir_id;
+
+ while let Some((parent_id, parent)) = iter.next() {
+ if let Some(x) = f(parent, child_id) {
+ return Some(x);
+ }
+ let parent = match parent {
+ Node::Expr(e) => e,
+ Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
+ child_id = parent_id;
+ continue;
+ },
+ Node::Arm(a) if a.body.hir_id == child_id => {
+ child_id = parent_id;
+ continue;
+ },
+ _ => return None,
+ };
+ match parent.kind {
+ ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
+ ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
+ child_id = id;
+ iter = map.parent_iter(id);
+ },
+ ExprKind::Block(..) => child_id = parent_id,
+ _ => return None,
+ }
+ }
+ None
+}
+