|
| 1 | +use std::ops::ControlFlow; |
| 2 | + |
| 3 | +use rustc_ast::visit::VisitorResult; |
| 4 | +use rustc_ast_ir::try_visit; |
| 5 | +use rustc_hir::intravisit::{self, Visitor}; |
| 6 | +use rustc_hir::{Block, Expr, ExprKind}; |
| 7 | + |
| 8 | +pub trait ReturnVisitor { |
| 9 | + type Result: VisitorResult = (); |
| 10 | + |
| 11 | + fn visit_implicit_return(&mut self, expr: &Expr<'_>) -> Self::Result { |
| 12 | + self.visit_return(expr) |
| 13 | + } |
| 14 | + |
| 15 | + fn visit_explicit_return(&mut self, expr: &Expr<'_>) -> Self::Result { |
| 16 | + self.visit_return(expr) |
| 17 | + } |
| 18 | + |
| 19 | + /// In the example below, this function will be called with the `{ todo!(); }` block |
| 20 | + /// after the `;` due to the `NeverToAny` adjustment leading to the block returning `u8` |
| 21 | + /// with no expression directly attributable. |
| 22 | + /// ```no_run |
| 23 | + /// fn example() -> u8 { |
| 24 | + /// { todo!(); } |
| 25 | + /// } |
| 26 | + /// ``` |
| 27 | + fn visit_diverging_implicit_return(&mut self, block: &Block<'_>) -> Self::Result; |
| 28 | + fn visit_return(&mut self, expr: &Expr<'_>) -> Self::Result; |
| 29 | +} |
| 30 | + |
| 31 | +struct ExplicitReturnDriver<V>(V); |
| 32 | + |
| 33 | +impl<V: ReturnVisitor> Visitor<'_> for ExplicitReturnDriver<V> { |
| 34 | + type Result = V::Result; |
| 35 | + type NestedFilter = intravisit::nested_filter::None; |
| 36 | + |
| 37 | + fn visit_expr(&mut self, expr: &Expr<'_>) -> Self::Result { |
| 38 | + if let ExprKind::Ret(Some(ret_val_expr)) = expr.kind { |
| 39 | + self.0.visit_explicit_return(ret_val_expr) |
| 40 | + } else { |
| 41 | + intravisit::walk_expr(self, expr) |
| 42 | + } |
| 43 | + } |
| 44 | +} |
| 45 | + |
| 46 | +fn visit_implicit_returns<V>(visitor: &mut V, expr: &Expr<'_>) -> V::Result |
| 47 | +where |
| 48 | + V: ReturnVisitor, |
| 49 | +{ |
| 50 | + let cont = || V::Result::from_branch(ControlFlow::Continue(())); |
| 51 | + match expr.kind { |
| 52 | + ExprKind::Block(block, _) => { |
| 53 | + if let Some(expr) = block.expr { |
| 54 | + visit_implicit_returns(visitor, expr) |
| 55 | + } else { |
| 56 | + visitor.visit_diverging_implicit_return(block) |
| 57 | + } |
| 58 | + }, |
| 59 | + ExprKind::If(_, true_block, else_block) => { |
| 60 | + try_visit!(visit_implicit_returns(visitor, true_block)); |
| 61 | + visit_implicit_returns(visitor, else_block.unwrap()) |
| 62 | + }, |
| 63 | + ExprKind::Match(_, arms, _) => { |
| 64 | + for arm in arms { |
| 65 | + try_visit!(visit_implicit_returns(visitor, arm.body)); |
| 66 | + } |
| 67 | + |
| 68 | + cont() |
| 69 | + }, |
| 70 | + |
| 71 | + _ => visitor.visit_implicit_return(expr), |
| 72 | + } |
| 73 | +} |
| 74 | + |
| 75 | +pub fn visit_returns<V>(visitor: V, expr: &Expr<'_>) -> V::Result |
| 76 | +where |
| 77 | + V: ReturnVisitor, |
| 78 | +{ |
| 79 | + let mut explicit_driver = ExplicitReturnDriver(visitor); |
| 80 | + try_visit!(explicit_driver.visit_expr(expr)); |
| 81 | + |
| 82 | + visit_implicit_returns(&mut explicit_driver.0, expr) |
| 83 | +} |
0 commit comments